math.h ceil在C中没有按预期工作

时间:2010-02-22 13:55:56

标签: c math compiler-construction floating-point

为什么ceil()会在没有小数部分的情况下进行均匀浮动? 当我尝试这样做时:

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

但是当我将2.22的值更改为2.21

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

我尝试用另一种方式使用modf()并遇到另一个奇怪的事情:

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...  

那么会发生什么是0.000 ...大于0?
谁能解释为什么这两种情况都会发生?此外,我正在使用RedHat中的cc版本4.1.2进行编译。

6 个答案:

答案 0 :(得分:12)

基本答案是你得到的浮点数:

double x = 2.22;

实际上比值2.22稍微大一点,以及

得到的值
double x = 2.21;

2.21小一点。

答案 1 :(得分:12)

这是正常的,因为数字是使用二进制存储的。虽然您的数字可以使用十进制数字写入有限数字,但这不适用于二进制数。

您应该阅读Goldberg的报告What Every Computer Scientist Should Know About Floating-Point Arithmetic

答案 2 :(得分:7)

并非所有浮点值都可以由floatdouble C类型正确表示 - 您认为222很可能实际上类似于222.00000000000000001。< / p>

有一种标准的,但鲜为人知的解决方法 - 使用nextafter() C99功能:

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

有关详细信息,请参阅man nextafter

答案 3 :(得分:2)

那是因为2.22不完全是2.22因此2.22 * 100不是222而是222.000000000x

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

如果你需要整数精度(例如计算金钱相关的东西),请使用积分算术(即计算美分而不是美元)。

答案 4 :(得分:1)

如果你要写:

const int x = 1.2;

在C程序中,会发生什么?文字1.2不能表示为整数值,因此编译器会根据通常的规则转换为整数,x将被赋值为1

同样的事情发生在这里。

您写道:

double x = 2.22;

2.22不能表示为双精度数,因此编译器会根据C标准中的规则对其进行转换。您将得到最接近的可表示的双精度数,正好是:

2.220000000000000195399252334027551114559173583984375

double中将此值乘以100时,结果为:

222.000000000000028421709430404007434844970703125

当您使用该值作为参数调用ceil( )时,数学库会正确返回223.0

答案 5 :(得分:0)

您可能需要考虑使用decimal floating point types来获得您期望的十进制算术结果。典型台式PC处理器上的硬件仅支持二进制浮点,因此不能期望产生与十进制计算相同的结果。当然,如果硬件不支持,则十进制浮点速度较慢。

请注意,十进制浮点不一定更精确(例如1/3不能精确表示,就像二进制FP中的值无法表示一样),它只会产生期望值结果,就像你用十进制长手计算一样。