Layman解释为什么JavaScript有奇怪的浮动数学 - IEEE 754标准

时间:2013-07-25 04:38:03

标签: javascript ieee-754

当我对浮点数进行数学运算时,我从来不知道JavaScript到底发生了什么。我一直都很害怕使用小数,直到我尽可能地避开它们。但是,如果我知道在IEEE 754标准中幕后发生了什么,那么我将能够预测会发生什么;具有可预测性,我会更自信,更不可怕。

有人可以给我一个简单的解释(简单解释整数的二进制表示关于IEEE 754标准如何工作以及它如何产生这种副作用:0.1 + 0.2 != 0.3

非常感谢! :)

5 个答案:

答案 0 :(得分:10)

0.1的小数分数不能在基数2中干净地表达

假设我们想在base-2中表示小数0.1。我们知道它等于1/10。在base-2中1除以10的结果是0.000110011001100...,其具有重复的小数序列​​。

因此,在十进制形式中,实际上很容易干净地表示像0.1这样的数字,在base-2中你无法准确地表达基于10的有理数。您只能通过使用能够存储的位数来近似它。

简单地说,我们只有足够的存储空间来重现该数字的第一个,例如8个有效二进制数字。存储的数字为11001100(指数为11)。这转换回基数为2的0.000110011,十进制为0.099609375,而不是0.1。这是将0.1转换为理论浮点变量时会发生的错误量,该变量将基值存储为8位(不包括符号位)。

浮点变量如何存储值

IEEE 754的标准规定了一种用二进制编码实数的方法,带有符号和二进制指数。指数应用于二进制域,这意味着在转换为二进制之前不会移位小数点,之后就会这样做。

有不同大小的IEEE浮点数,每个浮点数都指定了基数使用了多少二进制数字以及指数的数量。

当你看到0.1 + 0.2 != 0.3时,这是因为你实际上并没有在0.1或0.2上执行计算,而是在浮点二进制的这些数字的近似值上只有一定的精度。在将结果转换回十进制时,由于此错误,结果将不会精确为0.3。另外,结果甚至不等于0.3的二进制近似值。实际的错误量取决于浮点值的大小,因此使用了多少精度位。

四舍五入有时会有所帮助,但在这种情况下并非如此

在某些情况下,由于转换为二进制的精度损失而导致的计算错误将足够小,以便在从二进制转换回来的过程中从舍入值中消失,因此您甚至不会注意到任何差异 - 它将会看起来很有效。

IEEE浮点具有关于如何进行舍入的具体规则。

然而,对于0.1 + 0.2对0.3,舍入不会抵消错误。 添加0.1和0.2的二进制近似值的结果将与0.3的二进制近似值不同。

答案 1 :(得分:4)

如果您天真地将1/3转换为0.333(或任何有限数量的3),这与1/3 + 1/3 + 1/3!= 1的原因相同。 0.333 + 0.333 + 0.333 = 0.999,而非1。

在基数9(例如)中,1/3可以精确地表示为0.3 9 ,并且0.3 9 + 0.3 9 + 0.3 9 = 1.0 9 。 可以在基数9中精确表示的一些数字不能在基数10中精确表示,并且必须四舍五入到可以的数字。

类似地,某些数字不能在基数2中精确表示,但可以在基数10中表示,例如0.2。
0.2 10 是0.0011001100110011 ... 2
如果将其四舍五入为0.0011 2 ,则:
0.0011 2 + 0.0011 2 + 0.0011 2 + 0.0011 2 + 0.0011 2 < / sub> = 0.1111 2 ,而不是1.0000 2 。 (0.1111 2 是15/16)

由于计算机(至少是我们使用的计算机)以二进制方式进行算术运算,这会影响它们。

请注意,随着我们使用更多数字,结果的准确性会增加。 (0.33333333 10 + 0.33333333 10 + 0.33333333 10 = 0.99999999 10 ,这比0.999更接近正确答案<子> 10
由于这个原因,四舍五入的误差通常非常小。 double存储大约15个十进制数字,因此相对误差大约为10 -15 (更确切地说,2 -52 )。

因为错误很小,除非通常不会产生影响:

  • 您的计划需要非常高的准确度,或
  • 您使用大量小数位显示它(您可能会看到如0.99999999999999995622之类的数字)或
  • 您比较两个数字是否相等(使用==!=)。

比较非整数数字是否相等绝对是你应该避免的,但你可以在计算和其他比较(<>)中使用它们而不会出现问题(除非你的程序要求很高)精度)。

答案 2 :(得分:2)

在javascript中我总是这样做(Math.abs(.1 + .2-.3)&lt; .000001)

我一直都这么认为...... .25披萨+ .25披萨!= .5披萨(当你切它时你会丢掉披萨)lol

答案 3 :(得分:1)

如果你想使用花车有信心,请记住它们至少有15位有效数字,这对于常见任务来说几乎总是足够的。

日常工作所需的有效位数因人而异,例如:工程师可能只使用3,经济学家可能会使用5,科学家可能会使用更多(或更少)。首先计算出你想要的有效位数(例如,你想要看到2,345,876,234美元还是2.3亿美元)。如果它低于5位有效数字,你可以安全地使用至少7位有效数字运算,并将结果四舍五入到最后所需的有效位数。

e.g。如果您只需要3位有效数字:

(0.1 + 0.2).toFixed(3) // 0.300

如果你总是使用至少两个比你需要更多的有效数字,那么最后四舍五入到所需数字,你就不会被JavaScript数字引入的微小错误所困扰。

答案 4 :(得分:0)

以下链接对此进行了解释 Floating Point Representation Error