代码之家  ›  专栏  ›  技术社区  ›  Chet

为什么浮点错误会根据小数点的位置而改变?

  •  2
  • Chet  · 技术社区  · 6 年前

    我今天遇到了一个浮点错误(在javascript和python中验证过),这看起来很奇怪。

    > 15 * 19.22
    288.29999999999995
    

    这种情况真正奇怪的是,这些数字完全在浮点数表示的数字范围内。我们不是在处理非常大或非常小的数字。

    事实上,如果我简单地移动小数点,我就得到了正确的答案,而没有舍入误差。

    > 15 * 19.22
    288.29999999999995
    > 1.5 * 192.2
    288.29999999999995
    > .15 * 1922.
    288.3
    > 150 * 1.922
    288.3
    > 1500 * .1922
    288.3
    > 15 * 1922 / 100
    288.3
    > 1.5 * 1.922 * 100
    288.3
    > 1.5 * .1922 * 1000
    288.3
    > .15 * .1922 * 10000
    288.3
    

    显然,一定有一些中间数不能用浮点来表示,但这怎么可能呢?

    有没有一种“更安全”的方法来乘以浮点数来防止这个问题?我认为,如果数字的数量级相同,那么浮点乘法的计算结果最准确,但显然这是一个错误的假设。

    3 回复  |  直到 6 年前
        1
  •  3
  •   T.J. Crowder    6 年前

    为什么浮点错误会根据小数点的位置而改变?

    因为你在10垒工作。IEEE-754双精度二进制浮点工作于二进制(基2)。例如,在这种表述中, 1 可以精确地表示,但是 0.1 不能

    这种情况真正奇怪的是,这些数字完全在浮点数表示的数字范围内。我们不是在处理非常大或非常小的数字。

    正如你从我上面的陈述中看到的,即使是十分之一,你也会遇到不精确的数字,而不必去到令人不快的值(就像你得到9007199254740993这样不可表示的整数一样)。因此著名 0.1 + 0.2 = 0.30000000000000004 事情:

    console.log(0.1 + 0.2);

    有没有一种“更安全”的方法来乘以浮点数来防止这个问题?

    不使用内置浮点。您可能只使用整数(因为它们从-9007199254740992到9007199254740992是可靠的),然后在输出时插入相关的小数。您可能会发现这个问题的答案很有用: How to deal with floating point number precision in JavaScript? .


    你可能想知道为什么,如果 零点一 不完全代表, console.log(0.1) 输出 "0.1" . 这是因为在通常使用浮点的情况下,当转换为字符串时,只输出足够的数字来区分数字与其最近的可表示邻居。在情况下 零点一 只需要 “0.1” . 将二进制浮点转换为可表示的十进制十分复杂,请参阅 the spec . -)

        2
  •  1
  •   Rudy Velthuis    6 年前

    这是因为 二元的 浮点表示。取0.8、0.4、0.2和0.1的值作为双精度值。它们实际存储的值是:

    0.8 --> 0.8000000000000000444089209850062616169452667236328125
    0.4 --> 0.40000000000000002220446049250313080847263336181640625
    0.2 --> 0.200000000000000011102230246251565404236316680908203125
    0.1 --> 0.1000000000000000055511151231257827021181583404541015625
    

    正如您很容易看到的,每次将数字减半时,与精确的十进制值的差就减半。这是因为所有这些都有完全相同的意义,并且只有不同的指数。如果你看他们的十六进制表示,这就更清楚了:

    0.8 --> 0x1.999999999999ap-1
    0.4 --> 0x1.999999999999ap-2
    etc...
    

    所以 差异 在实际的、数学的值和实际存储的值之间的某个位置在最后一个位之内或之下。指数越低,该位的值越小。从另一个角度看:1.6是 0x1.999999999999ap+0 等等。你走得越高,越大 差额的价值 会变成,因为这个指数。这就是所谓相对误差的原因。

    如果你把 十进制的 关键是,你实际上改变了 二元的 指数也是。不完全是成比例的,因为我们处理的是不同的基数,但基本上是“等价的”(如果这是一个恰当的词)。数字越高,指数越高,因此数学值和浮点值之间的差值越大。

        3
  •  0
  •   Yves Daoust    6 年前

    不是一个答案,而是一个长的评论。

    数量级不是有罪的,因为浮点表示将数字标准化为1到2之间的尾数:

      15       = 1.875     x 2^3
      19.22    = 1.20125   x 2^4
     150       = 1.171875  x 2^7
       0.1922  = 1.5376    x 2^-3
    

    指数分别处理。