浮点算术中是否满足(1 + sqrt(2))^ 2 = 3 + 2*sqrt(2)?

Nax*_*mus 2 c++ math floating-point precision floating-accuracy

在数学中,身份(1 + sqrt(2))^2 = 3 + 2*sqrt(2)是正确的.但是在浮点(IEEE 754,使用单精度,即32位)计算情况并非如此,因为sqrt(2)没有二进制的精确表示.

那么使用近似值sqrt(2)为左侧和右侧提供不同的结果吗?如果是这样的话?对近似值求平方是否会显着降低精度?

然后哪个等效表达式给出最准确的结果?

Ste*_*non 14

当以IEEE-754双精度写入计算时,此标识恰好成立.原因如下:

两个正确舍入到双精度的平方根是:

sqrt(2) = 0x1.6a09e667f3bcd * 2^0
Run Code Online (Sandbox Code Playgroud)

(我在这里使用十六进制,因为表示更整洁,并且更容易翻译成IEEE754格式).如果没有发生溢出,乘以2的乘法在二进制浮点中是精确的,如本例所示,所以:

2*sqrt(2) = 0x1.6a09e667f3bcd * 2^1
Run Code Online (Sandbox Code Playgroud)

当我们添加三个时,我们得到:

3 + 2*sqrt(2) = 0x1.7504f333f9de68 * 2^2
Run Code Online (Sandbox Code Playgroud)

但是,这不是可表示的双精度数(它是一个太宽),因此结果四舍五入到最接近的可表示数.碰巧这个值正好在两个可表示的数字之间,所以我们选择一个尾随零位的值:

3 + 2*sqrt(2) = 0x1.7504f333f9de6 * 2^2
Run Code Online (Sandbox Code Playgroud)

现在是计算的另一面.当我们将一个加到2的双精度平方根时,我们得到:

1 + sqrt(2) = 0x1.3504f333f9de68 * 2^1
Run Code Online (Sandbox Code Playgroud)

这也是可表示的双精度数字之间的精确中间情况,并且再次舍入到最接近的"偶数"可表示数字:

1 + sqrt(2) = 0x1.3504f333f9de6 * 2^1
Run Code Online (Sandbox Code Playgroud)

当此值为平方时,结果为:

(1 + sqrt(2))*(1 + sqrt(2)) = 0x1.7504f333f9de599cacbc97eaa4 * 2^2
Run Code Online (Sandbox Code Playgroud)

这也不是可表示的双精度数.这个不是一个确切的中间情况,所以它只是舍入到最近的可表示数字,即:

(1 + sqrt(2))*(1 + sqrt(2)) = 0x1.7504f333f9de6 * 2^2
Run Code Online (Sandbox Code Playgroud)

总结:以两种不同的方式计算此值会产生两种不同的舍入序列,但最终结果是相同的.然而,我们只研究了双精度计算; 当使用不同的算术类型执行计算时可能不是这种情况.

但是,一般来说,表达式3 + 2*sqrt(2)应该更准确(在它们不同的情况下),因为对于任何二进制IEEE-754类型,它只产生两个舍入(平方根和加法),而(1 + sqrt(2))*(1 + sqrt(2))导致三个舍入(平方根,加法和乘法).还应注意,两者之间的差异最多为一位或两位,并且可能对您的目的而言可以忽略不计.


sth*_*sth 11

因为即使0.1 + 0.2 != 0.3您不应指望这种复杂的等式来保持有限的精度浮点数.

由于数字被四舍五入地存储到一定数量的二进制小数,如果数字(如0.1)将具有无限多个二进制数字,则它们不是精确的.因此,使用这些数字的计算结果也不准确,并且预期与计算的确切结果的微小差异.

  • 你不应该指望它,但事实证明,这个特定(不那么复杂)方程的两个方面在单精度浮点中完全相等. (3认同)