javascript 对于一个浮点数x,没有下溢和上溢,x+x和x*2的结果是相同的吗?

3zwtqj6y  于 2023-06-20  发布在  Java
关注(0)|答案(2)|浏览(90)

例如,最初我有这样的代码:

function(x,y){
  let z=x+y;
  .
  .
  .
}

后来我发现y必须和x一样,想把x+y重构为x * 2,但需要保证整个程序在重构前后的行为是一样的。x+x等于x*2吗?我不知道+和 * 是否使用不同的计算机制,从而导致舍入到不同的结果。
我测试了:

for(let i=0.01;i<100;i++){
  if(i+i!=i*2){
    console.log(i);
    break;
  }
}

似乎对i的某些范围是正确的,但不知道它是否对所有浮点数都是正确的。

cx6n0qe3

cx6n0qe31#

JavaScript是ECMAScript的实现,ECMAScript specification表示使用IEEE 754算法,具有IEEE-754“双精度”(binary 64)格式。第5.2.5条规定“...当应用于Numbers时,运算符指的是IEEE 754-2019中的相关操作......”
在IEEE 754和任何合理的浮点系统中,运算的结果是根据所选择的舍入规则(例如舍入到最近的连到偶数、向零舍入、向上舍入或向下舍入)舍入的精确数学结果。IEEE 754-2019 4.3说道:
...除非另有说明,否则执行每项运算时,应将其视为首先产生一个中间结果,该结果的精度可达无穷大且范围无限大,然后根据本条款中的一个属性对该结果进行四舍五入...
由于x + x和2·x具有相同的数学结果,因此浮点运算x+x2*x必须产生相同的计算结果。这两种方法将具有相同的数学结果,并且应用相同的舍入规则,因此计算结果必须相同。
以上涵盖了x是一个数的情况,包括+∞和−∞。如果xNaNx+x2*x也会产生NaN,所以结果也是一样的。(请注意,在这种情况下,x+x == 2*x将评估为false,因为NaN不等于任何东西,甚至不等于它本身。尽管如此,这两种操作产生相同的结果;如果使用X1 M12 N1 X代替X1 M13 N1 X,则编程行为将是相同的,反之亦然。

erhoui1w

erhoui1w2#

AFAICT,这些表达式将始终计算为相同的值。这是由IEEE754标准给出的,该标准规定计算的结果应该与以无限精度进行运算然后四舍五入到最接近的可表示数的结果相同。
作为经验性的健全性检查,我运行了以下Python代码:

import numpy as np
a = np.arange(2**24, dtype='uint32')
for i in range(256):
    b = np.frombuffer(a + (i << 24), 'float32')
    np.testing.assert_equal(b+b, b*2)

当它试图使用NaN和无穷大时,只得到了一些警告。这将在约20秒内彻底测试所有32位二进制浮点数。JavaScript使用64位浮点数,但考虑到它们受相同规则的约束,它应该是等效的。

相关问题