对于没有下溢和溢流的浮点数 x,x+x 和 x*2 的结果是否相同?

For a float number x, without underflow and overflow, is result of x+x and x*2 identical?

提问人:displaydisplayname 提问时间:6/19/2023 更新时间:6/19/2023 访问量:57

问:

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

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 的某些范围似乎是正确的,但不知道它是否适用于所有浮点数。

JavaScript 浮点

评论


答:

2赞 Sam Mason 6/19/2023 #1

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 位浮点数,但鉴于它们受相同规则的约束,它应该是等效的。

4赞 Eric Postpischil 6/19/2023 #2

JavaScript 是 ECMAScript 的实现,ECMAScript 规范规定使用 IEEE 754 算法,采用 IEEE-754“双精度”(binary64) 格式。第5.2.5条说:“......当应用于数字时,操作员指的是 IEEE 754-2019 中的相关操作......”

在 IEEE 754 和任何合理的浮点系统中,运算的结果是根据所选的舍入规则(例如舍入到最接近的平数、向零舍入、向上舍入或向下舍入)舍入的精确数学结果。IEEE 754-2019 4.3 说:

...除非另有说明,否则每个操作都应执行,就好像它首先产生一个精确到无限精度和无限范围的中间结果,然后根据本条款中的属性之一对该结果进行舍入......

由于 + 和 2• 具有相同的数学结果,因此浮点运算必须产生相同的计算结果。在应用相同的舍入规则的情况下,它们将具有相同的数学结果,因此计算结果必须相同。xxxx+x2*x

以上涵盖了数字的情况,包括 +∞ 和 −∞。如果是 ,并且还生成 ,则结果又是一样的。(请注意,在本例中,将评估为 false,因为 NaN 不等于任何事物,甚至不等于它本身。尽管如此,这两个操作产生相同的结果;如果用于代替,则程序行为将是相同的,反之亦然。xxNaNx+x2*xNaNx+x == 2*x2*xx+x

评论

0赞 vinc17 6/23/2023
为了完成答案,这也是零的情况:both 和 将给出一个带有符号的零。如果是 NaN,则可能不会获得相同的 NaN(不一定是相同的有效负载,也不一定是相同的符号位),但 Javascript 代码无法检测到差异。但是,请参阅是否允许 JS 引擎更改 NaN 的位?xx+xx*2xx