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