【问题标题】:Why is 9999999999999999 converted to 10000000000000000 in JavaScript?为什么 9999999999999999 在 JavaScript 中被转换为 10000000000000000?
【发布时间】:2012-11-17 09:49:43
【问题描述】:

谁能给我解释一下为什么 9999999999999999 被转换为 10000000000000000?

alert(9999999999999999); //10000000000000000

http://jsfiddle.net/Y2Vb2/

【问题讨论】:

  • 该数字太大而无法放入整数,因此可能会转换为双精度数。浮点数不准确。
  • 正如@Kos 所说,所有 javascript 数字都是双精度浮点数,这意味着您只有 16 位精度,而 9999999999999999 显然超过了这个限制
  • 相关:在javascript中可以100%精度使用的最高整数是+/- 9007199254740992(或Math.pow(2,53)):stackoverflow.com/questions/307179/…

标签: javascript


【解决方案1】:

Javascript 没有整数,只有 64 位浮点数 - 而且您已经用完了浮点精度。

查看 Java 中的类似问题:why is the Double.parseDouble making 9999999999999999 to 10000000000000000?

【讨论】:

    【解决方案2】:

    为什么 9999999999999999 会转换为 10000000000000000 ?

    JavaScript 中的所有数字都以 64 位格式 IEEE-754 存储,也称为“双精度”,因此正好有 64 位来存储一个数字:其中 52 位用于存储数字,其中11个存储小数点的位置(整数为零),1位用于符号

    如果一个数字太大,它会溢出 64 位存储,可能会导致 无穷大:

    alert( 1e500 );  
    // Result => Infinity
    // "e" multiplies the number by 1 with the given zeroes count.
    

    如果我们检查 0.1 和 0.2 的和是否为 0.3,我们会得到错误。

    alert( 0.1 + 0.2 == 0.3 )
    

    奇怪!如果不是 0.3,那么它是什么? 这是因为,一个数字以二进制形式存储在内存中,即一系列 1 和 0。但是像 0.1、0.2 这样的分数在十进制数值系统中看起来很简单实际上是二进制形式的无休止分数。

    换句话说,什么是 0.1?它是一除以十的 1/10,十分之一。在十进制数字系统中,这些数字很容易重新表示。将其与三分之一进行比较:1/3。它变成了一个无穷小数 0.33333(3)。

    无法使用二进制系统准确存储 0.1 或 0.2,就像无法将三分之一存储为小数一样。

    数字格式 IEEE-754 通过四舍五入到最接近的可能数字解决了这个问题。这些四舍五入规则通常不允许我们看到“微小的精度损失” ,所以数字显示为 0.3。但请注意,损失仍然存在。

    如你所见:

    alert( 9999999999999999 ); // shows 10000000000000000
    

    这有同样的问题:精度损失。数字有 64 位,其中 52 位可用于存储数字,但 这还不够。所以最低有效数字消失了。

    9999999999999999 到 10000000000000000 背后真正发生的事情是:

    JavaScript 不会在此类事件中触发错误。它尽最大努力将数字拟合成所需的格式,但不幸的是,这种格式还不够大。

    参考:https://javascript.info/number

    你也可以参考这个SO Question,它包含了关于 JavaScript 编号的非常详细的信息。

    【讨论】:

      【解决方案3】:

      9999999999999999二进制形式为100011100001101111001001101111110000001111111111111111 有 54 位数字。

      下面我们将把这个数字转换为 Javascript IEEE-754,它有 1 位符号, 11 位二进制偏移格式尾数和 52 位符号 编号本身。

      在二进制形式中,第一个数字始终是 1,因此 Javascript 在保存为 IEEE-754 格式时会省略尾数中数字的第一个数字。 So, we will have 00011100001101111001001101111110000001111111111111111 for mantissa, which is 53 digits and as for the number we can keep only 52 digits we round the number removing last digit 0001110000110111100100110111111000001000000000000000

      二进制形式的最终数字将为 1 0001110000110111100100110111111000001000000000000000 0 十进制形式为 10000000000000000

      1 是尾数中未写入 52 位的第一个数字,则 52 位尾数和一个 0 使其返回 54 位,即 10000000000000000十进制

      除非您阅读此beautiful article

      ,否则这可能很难理解

      【讨论】:

        【解决方案4】:

        9999999999999999 在 JavaScript 内部被视为浮点数。它不能在 IEEE 754 双精度中准确表示,因为它需要 54 位精度(位数为 log2(9999999999999999) = 53,150849512 ......并且由于不存在小数位,因此必须将结果向上取整)而 IEEE 754 仅提供 53 位(1 个隐含位 + 52 个显式存储的尾数位) - 少了一位。因此,数字只是四舍五入。

        由于在这种情况下只丢失了一位,因此即使是 54 位数字也可以精确表示,因为它们仍然在该位中包含0,这会丢失。考虑到 IEEE 754 的默认无偏舍入模式,奇数 54 位数字被舍入到恰好是 53 位偶数的两倍的最接近的值。

        【讨论】:

        • 奇数并不总是四舍五入。通常的做法是使用银行家的四舍五入,因此它们被四舍五入到最接近的四的倍数。 Google Chrome 控制台中的快速测试证实了这一点。
        • IEEE 有几种不同的舍入模式,但默认情况下(这是 JS 使用的)它会将中途情况舍入到最接近的偶数二进制数。在某些情况下,这是四的倍数,但并非总是如此。
        【解决方案5】:
        1. JavaScript 只有浮点数,没有整数。

        2. 阅读What Every Computer Scientist Should Know About Floating-Point Arithmetic

          总结:浮点数只包括有限的精度,超过 15 位(左右),你会得到四舍五入。

        【讨论】:

          【解决方案6】:

          问题:有时 JavaScript 计算似乎会产生“不准确”的结果,例如0.362*100 产生 36.199999999999996。我怎样才能避免这种情况?

          答案:JavaScript 在内部以双精度浮点格式存储所有数字,具有 52 位尾数和 11 位指数(用于存储数值的 IEEE 754 标准)。这种数字的内部表示可能会导致上述意外结果。大多数大于 253 = 9007199254740992 的整数不能以这种格式精确表示。同样,许多小数/分数(例如 0.362)无法准确表示,导致上述示例中的感知“不准确”。为避免这些“不准确”的结果,您可能需要将结果四舍五入到您使用的数据的精度。

          http://www.javascripter.net/faq/accuracy.htm

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2023-03-24
            • 1970-01-01
            • 2019-03-24
            • 2011-02-09
            • 1970-01-01
            • 2021-07-01
            • 2019-09-19
            相关资源
            最近更新 更多