大多数语言无法正确地绕31.45

时间:2016-06-07 12:25:42

标签: floating-point

我们有这个输入字段,用户应输入百分比。我们显示1位小数的百分比。如果用户选择输入31.45,我们注意到浮点表示为31.44999999999,格式正确为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;
}

输出:

31.45000076293945312500
31.450
31.45
31.5
31
31.44999999999999928946
31.450
31.45
31.4
31

请注意,float正确处理它,而double则没有!

我在Python中尝试过同样的事情:

>>> "%.1f" % 31.45
'31.4'

我尝试使用Wolfram Alpha:http://www.wolframalpha.com/input/?i=round+31.45+to+nearest+.1:同样的结果。

Obj-C中的NSNumberFormatter也存在同样的问题。

Excel正确处理它!使用一位小数时,31.45的格式为31.5。

我不确定该怎么想。我想象字符串格式化程序足够聪明,可以处理浮点值的限制,但似乎大部分都没有。任何提示如何以通用方式处理这个?另外,在这种情况下,为什么浮点数的行为优于双倍,或者它只是&#34;运气&#34;?

4 个答案:

答案 0 :(得分:4)

由于31.4500007629394531250031.44999999999999928946之间存在差异,他们不会错误地阅读您的测试。

重点是使用floor / ceil / round并阅读文档来询问您想要的内容,以确保您知道自己将会得到什么。

答案 1 :(得分:4)

这是double rounding的结果。首先,31.45四舍五入到最接近的可表示double,这正是31.449999999999999289457264239899814128875732421875。然后,当你打印时,你将它舍入到三个小数位,给出31.4(无论关系是否舍入为偶数 - 这不是平局!)。

如果您希望浮点值的行为符合您的预期,请使用decimal floating-point type

答案 2 :(得分:0)

您应该了解浮点数仅为您提供十进制数的近似值。

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

我希望你知道你不应该self.env['csm'].create({})吗?相反,你需要做if (floatA == floatB)(其中EPSILON是如上所述的最小单位)

同样,在你想要一个&#34;精确的&#34;四舍五入,你应该做if (floatA > floatB - EPSILON && floatA < floatB + EPSILON)

之类的事情

答案 3 :(得分:-4)

这与精度无关,但使用的舍入算法。大多数语言使用round half to even舍入而不是像round half up这样的偏向舍入。例如:

GNU Libc

  

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

.NET

  

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

     

远离零

     

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

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