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

在C语言中,math.h ceil没有按预期工作,c,math,compiler-construction,floating-point,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 exp

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

基本答案是,使用以下方法得到的浮点数:

double x = 2.22;
实际上比值
2.22
大一点点,并且比您得到的值大一点点

double x = 2.21;

略小于
2.21

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


你应该读一下戈德堡的报告。

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

如果您需要整数精度(例如计算货币相关的东西),请使用整数算法(即计算美分而不是美元).

并非所有浮点值都可以用
float
double
C类型正确表示-很可能是您认为的
222
实际上类似于
222.0000000000000000 1

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

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

<>代码> man NEXTAFFTER < /代码>细节。

< P>您可能想考虑使用以获得十进制算术所期望的结果。典型桌面PC处理器上的硬件仅支持二进制浮点,因此不能期望产生与十进制计算相同的结果。当然,如果硬件不支持,十进制浮点运算的速度会慢一些

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

如果您要写:

const int x = 1.2;
在C程序中,会发生什么?文本
1.2
不能表示为整数值,因此编译器将根据常规规则将其转换为整数,
x
将被分配值
1

同样的事情也在这里发生

你写道:

double x = 2.22;
2.22
不能表示为双精度数字,因此编译器会根据C标准中的规则对其进行转换。您将获得最接近的可表示双精度数字,即:

2.220000000000000195399252334027551114559173583984375
当该值乘以100 in
double
时,结果为:

222.000000000000028421709430404007434844970703125

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

Duplicates:,有趣!另请参见“nearbyint”和“rint”。@Jonathan
nearbyint()
舍入为整数,
nextafter()
允许您指定方向-
无穷大、
0
-无穷大
或任意数字。是的,我认识到这一点。但“nextafter()”生成尾数最低有效位调整为1的浮点值:“根据方向添加或减去与尾数最低有效位对应的值”。只要最低有效位的错误不超过1,“nextafter()”函数就可以工作,这里可能就是这种情况。我并不是说“nearbyint”或“rint”是“nextafter”(或“nexttoward”)的替代品;只是它们也很有趣,可能与OP有关。请参见
nextafter
文档:建议金融应用程序使用整数或定点算法是一个常见错误,实际上使用十进制浮点(与二进制浮点相反)。对于百分比计算(例如利息或税收),以美分为单位计算将导致出现超人III/办公空间场景!;-)@Clifford:你有关于如何使用十进制浮点修正超人III/办公空间场景的指针吗?(我不是说不可能——我真的很好奇)。@Clifford-AFAIK GnuCash正在使用定点算法,所以我怀疑正确使用定点是不可能的。@Micheal Burr:不,我只是想在那里的某个地方找到电影参考。因为两者都是虚构的作品,所以它可能是脆弱的。真正的问题是以一美分(或一便士或任何最小的货币单位)为单位工作。这并非不可能,但你需要更高的精度,而仅仅是美分。然后你也需要足够的范围。每个人都会指向那篇文章,但它不是很好。你最好读维基百科