Alexandr Poltavsky
Software Developer
Location: Russia, Moscow
poltavsky.alexandr@gmail.com
Blog
Github
Shadertoy
Twitter
Floating Point Rounding
Asked myself, "What is the best way to round a floating point?" Looked up an answer.
People note that using the straightforward way of adding +0.5 and truncating breaks in some cases, including negative numbers.
While thinking about it I came up with a simple and stable solution that works in all those cases. And it's certainly easier than this solution by "pascal".
#include <stdio.h>
#include <stdint.h>
double n0 = 0.5;
double n1 = -0.5;
//one ULP below 0.5, breaks naive rounding
double n2 = 0.49999999999999994;
double n3 = -0.49999999999999994;
int32_t round_naive( double n ) {
return n + 0.5;
}
int32_t round_less_naive( double n ) {
return n > 0.0 ? n + 0.5 : n - 0.5;
}
int32_t round_my_try( double n ){
int32_t n2 = (int32_t)( n * 2.0 ); // double it
int bit = n2 & 0x1; // extract the half-bit
bit = n2 >> 31 ? -bit : bit; // check sign
return n2/2 + bit; // adjust
}
void test( double n ) {
printf( "round_naive ( %.17f ) -> %d\n", n, round_naive( n ) );
printf( "round_less_naive ( %.17f ) -> %d\n", n, round_less_naive( n ) );
printf( "round_my_try ( %.17f ) -> %d\n\n", n, round_my_try( n ) );
}
int main(void) {
test( n0 );
test( n1 );
test( n2 );
test( n3 );
return 0;
}
Output:
round_naive ( 0.50000000000000000 ) -> 1
round_less_naive ( 0.50000000000000000 ) -> 1
round_my_try ( 0.50000000000000000 ) -> 1
round_naive ( -0.50000000000000000 ) -> 0 //<--wrong
round_less_naive ( -0.50000000000000000 ) -> -1
round_my_try ( -0.50000000000000000 ) -> -1
round_naive ( 0.49999999999999994 ) -> 1 //<-- wrong
round_less_naive ( 0.49999999999999994 ) -> 1 //<-- wrong
round_my_try ( 0.49999999999999994 ) -> 0
round_naive ( -0.49999999999999994 ) -> 0
round_less_naive ( -0.49999999999999994 ) -> -1 //<--wrong
round_my_try ( -0.49999999999999994 ) -> 0
Notice that I explicitly use int32_t, that limits the range of values. If you need, try int64_t. This method also works for large floats (if using floats in place of doubles) like 8388609.0f.