math.h ceil 未按 C 中的预期工作

问题描述 投票:0回答:6

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

c math floating-point
6个回答
12
投票

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

您应该阅读 Goldberg 的报告,名为“每个计算机科学家应该了解浮点运算”。


12
投票

double x = 2.22;

 实际上比值 
2.22

稍大一点,而你用

 得到的值

double x = 2.21;

2.21

小一点点。

    


7
投票
float

double
C 类型正确表示 - 最有可能的是您认为的
222
实际上类似于
222.00000000000000001

有一个标准但鲜为人知的方法来解决它 - 使用

nextafter()

C99 函数:


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

详情请参阅
man nextafter

    


2
投票

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

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


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
    


0
投票
十进制浮点类型

来获得您期望的十进制算术结果。 典型台式电脑处理器上的硬件仅支持二进制浮点,因此不能期望产生与十进制计算相同的结果。 当然,如果硬件不支持的话,十进制浮点数会比较慢。 请注意,十进制浮点数不一定更精确(例如 1/3 无法精确表示,就像二进制 FP 中存在无法表示的值一样),它只会产生

expected

结果,就好像你执行了十进制长手计算。

© www.soinside.com 2019 - 2024. All rights reserved.