考虑以下代码:

0.1 + 0.2 == 0.3  ->  false
0.1 + 0.2         ->  0.30000000000000004

为什么会出现这些错误?


当前回答

除了其他正确答案之外,您可能还需要考虑缩放值以避免浮点运算的问题。

例如:

var result = 1.0 + 2.0;     // result === 3.0 returns true

…而不是:

var result = 0.1 + 0.2;     // result === 0.3 returns false

在JavaScript中,表达式0.1+0.2===0.3返回false,但幸运的是,浮点中的整数运算是精确的,因此可以通过缩放来避免十进制表示错误。

作为一个实际的例子,为了避免精度至关重要的浮点问题,建议1将钱作为一个整数来处理:2550美分而不是25.50美元。


1 Douglas Crockford:JavaScript:好的部分:附录A——糟糕的部分(第105页)。

其他回答

另一种方法是:使用64位来表示数字。因此,无法精确表示超过2**64=18446744073709551616个不同的数字。

然而,Math表示,在0和1之间已经有无限多的小数。IEE 754定义了一种编码,以有效地将这64位用于更大的数字空间加上NaN和+/-无穷大,因此在精确表示的数字之间存在间隙,只填充近似的数字。

不幸的是,0.3存在差距。

它被打破的方式与你在小学学习并每天使用的十进制(以10为基础)表示法完全相同,只是以2为基础。

要理解,请考虑将1/3表示为十进制值。这是不可能做到的!世界将在你写完小数点后的3之前结束,所以我们写了一些地方,认为它足够准确。

以同样的方式,1/10(十进制0.1)不能以2为基数(二进制)精确地表示为“十进制”值;小数点后的重复模式将永远持续下去。该值不精确,因此无法使用常规浮点方法对其进行精确计算。与基数10一样,还有其他值也显示了这个问题。

这些奇怪的数字之所以出现,是因为计算机使用二进制(以2为基数)数字系统进行计算,而我们使用十进制(以10为基数)。

大多数分数不能用二进制或十进制或两者精确表示。结果-四舍五入(但精确)的数字结果。

一些统计数据与这个著名的双精度问题有关。

当使用0.1(从0.1到100)的步长将所有值(a+b)相加时,精度误差的概率约为15%。请注意,该错误可能会导致稍大或稍小的值。以下是一些示例:

0.1 + 0.2 = 0.30000000000000004 (BIGGER)
0.1 + 0.7 = 0.7999999999999999 (SMALLER)
...
1.7 + 1.9 = 3.5999999999999996 (SMALLER)
1.7 + 2.2 = 3.9000000000000004 (BIGGER)
...
3.2 + 3.6 = 6.800000000000001 (BIGGER)
3.2 + 4.4 = 7.6000000000000005 (BIGGER)

当使用0.1(从100到0.1)的步长减去所有值(a-b,其中a>b)时,我们有大约34%的精度误差。以下是一些示例:

0.6 - 0.2 = 0.39999999999999997 (SMALLER)
0.5 - 0.4 = 0.09999999999999998 (SMALLER)
...
2.1 - 0.2 = 1.9000000000000001 (BIGGER)
2.0 - 1.9 = 0.10000000000000009 (BIGGER)
...
100 - 99.9 = 0.09999999999999432 (SMALLER)
100 - 99.8 = 0.20000000000000284 (BIGGER)

*15%和34%确实是巨大的,所以当精度非常重要时,请始终使用BigDecimal。使用2个十进制数字(步骤0.01),情况会进一步恶化(18%和36%)。

你试过胶带解决方案了吗?

尝试确定错误发生的时间,并用简短的if语句修复它们,这并不漂亮,但对于某些问题,这是唯一的解决方案,这就是其中之一。

 if( (n * 0.1) < 100.0 ) { return n * 0.1 - 0.000000000000001 ;}
                    else { return n * 0.1 + 0.000000000000001 ;}    

我在c#的一个科学模拟项目中也遇到过同样的问题,我可以告诉你,如果你忽视蝴蝶效应,它会变成一条大胖龙,咬你一口**