简体   繁体   中英

math.h ceil not working as expected in C

How come ceil() rounds up an even floating with no fractional parts?
When I try to do this:

double x = 2.22;  
x *= 100; //which becomes 222.00...  
printf("%lf", ceil(x)); //prints 223.00... (?)  

But when I change the value of 2.22 to 2.21

x *= 100; //which becomes 221.00...  
printf("%lf", ceil(x)); //prints 221.00... as expected  

I tried to do it another way like this using modf() and encountered another strange thing:

double x = 2.22 * 100;  
double num, fraction;  
fraction = modf(x, &num);  
if(fraction > 0)  
    num += 1; //goes inside here even when fraction is 0.00...  

So what happens is 0.000... is greater than 0?
Can anyone explain why both of these situations are happening? Also I'm compiling using cc version 4.1.2 in RedHat.

The basic answer is that the floating point number you get with:

double x = 2.22;

is actually a tiny bit larger than the value 2.22 , and the value you get with

double x = 2.21;

is a tiny bit smaller than 2.21 .

This is normal as numbers are stored using binary. While your numbers can be written in finite numbers of digits using decimal, this does not hold for binary.

You should read Goldberg's report called What Every Computer Scientist Should Know About Floating-Point Arithmetic .

Not all floating-point values can be be correctly represented by float and double C types - most likely what you think is 222 actually something like 222.00000000000000001 .

There is a standard, but little-known way to get around it - use nextafter() C99 function:

printf("%lf", ceil(nextafter(x, 0)));

See man nextafter for details.

That's because 2.22 is not exactly 2.22 and thus 2.22 * 100 is not 222 but 222.000000000x

double x = 2.22;  
printf("%.20lf\n", x); //prints 2.22000000000000019540
x *= 100; 
printf("%.20lf\n", x); //prints 222.00000000000002842171

If you need integer precision (eg to compute money related stuff) use integral arithmetic (ie compute cents instead of dollars).

If you were to write:

const int x = 1.2;

in a C program, what would happen? The literal 1.2 isn't representable as an integer value, so the compiler would convert according to the usual rules to an integer, and x would be assigned the value 1 .

The same thing is happening here.

You wrote:

double x = 2.22;

2.22 is not representable as a double-precision number, so the compiler converts it according to the rules in the C standard. You get the closest representable double-precision number, which is exactly:

2.220000000000000195399252334027551114559173583984375

When this value is multiplied by 100 in double , the result is:

222.000000000000028421709430404007434844970703125

and when you call ceil( ) with that value as an argument, the math library correctly returns 223.0 .

You may want to consider using decimal floating point types to get the results you'd expect from decimal arithmetic. The hardware on typical desktop PC processors supports only binary floating point, so cannot be expected to produce the same results as a decimal calculation. Of course, if not supported in hardware, decimal floating point is slower.

Note that decimal floating point is not necessarily more precise (for example 1/3 cannot be represented precisely, just as there are values in binary FP that cannot be represented), it will just produce the expected result, as if you performed the calculation long-hand in decimal.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM