Floating point 大多数语言不能正确地将31.45舍入

Floating point 大多数语言不能正确地将31.45舍入,floating-point,Floating Point,我们有一个输入字段,用户应该在其中输入一个百分比。我们用1个小数点显示百分比。如果用户选择输入31.45,我们注意到浮点表示为31.449999999,格式正确为31.4,但正确的数学舍入规则表示原始数值应舍入为31.5 所以我寻找类似的问题,但大多数答案都说字符串格式化程序应该正确处理这种情况。然而,这并不适合我 我尝试了以下C代码: #include <math.h> #include <stdio.h> int main(int argc, char* argv[

我们有一个输入字段,用户应该在其中输入一个百分比。我们用1个小数点显示百分比。如果用户选择输入31.45,我们注意到浮点表示为31.449999999,格式正确为31.4,但正确的数学舍入规则表示原始数值应舍入为31.5

所以我寻找类似的问题,但大多数答案都说字符串格式化程序应该正确处理这种情况。然而,这并不适合我

我尝试了以下C代码:

#include <math.h>
#include <stdio.h>

int main(int argc, char* argv[])
{
    float x = 31.45;

    printf("%.20f\n", x);
    printf("%.3f\n", x);
    printf("%.2f\n", x);
    printf("%.1f\n", x);
    printf("%.0f\n", x);

    double y = 31.45;

    printf("%.20f\n", y);
    printf("%.3f\n", y);
    printf("%.2f\n", y);
    printf("%.1f\n", y);
    printf("%.0f\n", y);

    return 0;
}
请注意,float可以正确处理它,而double不能

我在Python中尝试了同样的方法:

>>> "%.1f" % 31.45
'31.4'
我尝试了Wolfram Alpha::同样的结果

Obj-C中的NSNumberFormatter也有同样的问题

不过Excel处理得很正确!使用一位小数时,31.45的格式为31.5


我不知道该怎么想。我曾想象字符串格式化程序会足够聪明,能够处理浮点值的限制,但似乎大多数都不能。有没有关于如何以通用方式处理此问题的提示?另外,在这种情况下,为什么浮点数的性能比double好,有什么逻辑吗?或者这只是“运气”

它们没有,你只是错误地读取了测试,因为
31.45000076293945312500
31.44999999999298946
之间存在差异


要点是通过使用
floor
/
ceil
/
round
并阅读文档来询问您想要什么。这与精度无关,但与使用的舍入算法无关。大多数语言使用舍入,而不是有偏差的舍入,例如。例如:

:

如果结果介于两个可表示值之间,则选择偶数可表示值

:

Round方法支持两种处理中点值的舍入约定:

从零开始四舍五入

四舍五入到最近值,或银行家四舍五入

您可以在上阅读有关舍入方法的更多信息。

这是由于。首先,
31.45
四舍五入到最接近的可表示的
double
,即
31.44999999298945726423989414128875732421875
。然后,在打印时,将其四舍五入到小数点后三位,得到
31.4
(不管领带是否四舍五入到偶数,这不是领带!)


如果您希望浮点数的行为与十进制算术的预期一致,请使用。

您应该了解,浮点数只提供十进制数的近似值

处理近似值引起的细微差异的一种常见方法是确定应用程序需要支持的小数位数,并使用最小的单位作为算术调整

我希望您知道,如果(floatA==floatB),您不应该这样做?相反,您需要执行
if(floatA>floatB-EPSILON&&floatA
(其中EPSILON是如上所述的最小单位)


类似地,在需要“精确”舍入之前,应该执行类似于
printf(“%.20f\n”,y+EPSILON)的操作

FWIW,如果您想要更好的精度和可预测的舍入,请使用整数并对其进行缩放,或者使用已经为您执行此操作的(第三方)类。我不同意您的看法。任何低于31.45的值都应该四舍五入到31.4,所以31.449999应该四舍五入。请注意,打破僵局的规则(无论是哪一条)只适用于下一个较高的小数点的一半,而不适用于低于或高于小数点的任何内容。正如其他人所说:这(31.44999…)不是平局,因此平局打破规则不适用。请参阅,我也不确定我是否遵循您的要求,但我确实理解,浮点和双精度类型恰好以不同的方式表示,因此,浮点的舍入效果与我预期的一样。对于Wolfram Alpha,我要求四舍五入到最接近的0.1,这不是我所期望的,但我认为Selcuks的回答解释了原因。但在这种情况下,关系的四舍五入方式无关紧要,因为给定的值不是关系!对于
double
值,从四舍五入到偶数和从四舍五入到上的正确结果将是
31.4
。事实上,这就是Python 2和Python 3所给出的结果,即使Python 2的
round
使用远离零的圆结,而Python 3的
round
使用圆结来实现偶数。@Jonatan:是的,正是因为浮点舍入错误。对于二进制浮点,您看到的不是您得到的!在代码中键入双字符
31.45
时,实际存储的值是(在典型机器上)
31.4499999929288945726423989814128875732421875
。因此,该值已经小于31.45的中间值,取整算法正确地将其取整为31.4。@Jonatan:这将是非常不精确的,而且是DWIM-ish。IEEE 754的全部内容都基于这样一个理念,即浮点值具有精确的值,所有计算(包括基转换)都应在该精确值上执行。这使它变得清晰、简单和可预测。你的建议会使事情变得更加模糊。关键的一点是你使用了“非常接近”这个词:对于“非常接近”这个词,没有一个选项适合所有的应用程序,如果补上一个,那将是一个错误。@Jonatan:什么是“非常接近”?
0.3-0.2-0.1=-2.77555756156289135105907917022705078125e-17
是否“非常接近”零?阿伏伽德罗数的倒数是多少?普朗克常数呢?你认为每个人都应该将其中哪一个视为零,你如何编写代码来做到这一点,以及这对化学、物理或工程有什么影响?@Jonatan:为这个问题道歉,但你为什么希望计算机对你撒谎?谢谢。这是我们在iOS以外的平台上所做的,在iOS以外的平台上,NSNumberFormatter似乎在做什么
>>> "%.1f" % 31.45
'31.4'