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

为什么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进行编译。

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

您应该阅读Goldberg的报告,名为“每个计算机科学家应该知道的关于浮点算术的内容” 。

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

 double x = 2.22; 

实际上是一个比2.22更大的值,以及你得到的值

 double x = 2.21; 

2.21小一点。

并非所有浮点值都可以由floatdouble C类型正确表示 – 很可能你认为222实际上是222.00000000000000001

有一种标准的,但鲜为人知的解决方法 – 使用nextafter() C99函数:

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

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

那是因为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 

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

如果你要写:

 const int x = 1.2; 

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

同样的事情发生在这里。

你写了:

 double x = 2.22; 

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

 2.220000000000000195399252334027551114559173583984375 

当此值乘以100为double ,结果为:

 222.000000000000028421709430404007434844970703125 

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

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

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