为什么/如何 (-1) ** 0.5 不准确?

Why/how is (-1) ** 0.5 inaccurate?

提问人:Kelly Bundy 提问时间:7/16/2022 更新时间:7/19/2022 访问量:155

问:

Python 计算虚数单位 i = sqrt(-1) 不准确:

>>> (-1) ** 0.5
(6.123233995736766e-17+1j)

应该是确切的(Python 称其为 j 而不是 i)。-1 和 0.5 都是精确表示的,结果也可以完全表示,因此 Python 无法正确表示它没有硬性理由(即浮点限制)。它可以。而 i=sqrt(-1) 的定义使得 Python 弄错了这一点相当令人失望。那么为什么会这样呢?它如何计算不准确的结果?1j

Python 复数 浮点精度

评论

3赞 njuffa 7/16/2022
一般来说,从数值的角度来看,通过泛型幂计算平方根是一个坏主意™,无论操作数是实数还是复数。原因是泛型幂使用比平方根更复杂的算法,这通常会导致更大的误差范围。此处实部分量的大小约为双精度舍入误差的数量级。
5赞 Eric Postpischil 7/16/2022
I 猜想的计算方式大致为 log(−1) 为 πi,π 不能用浮点格式表示,因此四舍五入为 3.141592653589793115997963468544185161590576171875,然后计算为 6.12323399573676603586882014729198302312846062338790031898128063403419218957424163818359375•10^−17 + i。(-1) ** 0.5exp(log(-1) * .5)exp(3.141592653589793115997963468544185161590576171875/2*i)
0赞 chux - Reinstate Monica 7/16/2022
@EricPostpischil 很好地使用了精确值。

答:

1赞 Eric Postpischil 7/19/2022 #1

当需要复杂的算术运算时,Python 实现可能会将 x y 计算e y ln x,就像使用复杂的 C 函数和 一样。反过来,它们很可能是用实函数计算的,包括 、 、 、 和 ,但细节不需要我们关心。cexpcloglnsqrtatan2sincospow

ln −1 为 πi。但是,π不能以浮点格式表示。您的 Python 实现可能使用 IEEE-754“双精度”格式,也称为 binary64。在该格式中,最接近π的可表示值是 3.141592653589793115997963468544185161590576171875。因此,ln −1 可能计算为 3.141592653589793115997963468544185161590576171875 i。

那么 y ln x = .5 • 3.141592653589793115997963468544185161590576171875 i 是 1.5707963267948965579989817342720925807952880859375 i。

e1.5707963267948965579989817342720925807952880859375 我也不完全可表示。其真实值约为 6.123233995736765886130329661375001464640•10−17 + .9999999999999999999999999999999981253003 i。

与 6.123233995736765886130329661375001464640•10−17 最接近的可表示值为 6。 12323399573676603586882014729198302312846062338790031898128063403419218957424163818359375•10−17,最接近.9999999999999999999999999999999981253003的可表示值为1,因此计算结果为6.12323399573676603586882014729198302312846062338790031898128063403419218957424163818359375•10−17 + 1 i。

评论

0赞 Kelly Bundy 7/20/2022
我现在追踪了代码。我相信它经过了 float_pow(它做了相当多的大小写区分,但不完全是 (-1)^0.5) 并最终将实部计算为 pow(hypot(-1, 0), 0.5) * cos(atan2(0, -1) * 0.5) (参见 Python 演示)。
0赞 Kelly Bundy 7/20/2022
因此,它可以添加另一个案例区别来识别这个值得注意的案例,但我想这是不值得的。顺便说一句,你是如何得到这些近似真实值的?WolframAlpha 左右?
0赞 Eric Postpischil 7/21/2022
@KellyBundy:我使用 Maple 10 作为 e^(1.57...375 i)。我在 macOS 上使用 Apple 的工具来计算浮点值,在这些浮点值中,浮点值很容易在浮点运算中计算;Apple 在确保格式正确转换数字方面做得很好。在需要更精确的地方,我通常使用旧版本的 Maple 10。printf
0赞 Kelly Bundy 7/21/2022
Hmm, is it noteworthy that that works correctly? I expect it to. Is some other implementation not working correctly? Can you tell an example?printf
0赞 Eric Postpischil 7/21/2022
@KellyBundy: The C standard only requires it to “work” up to a certain number of digits. From memory, the conversion has to produce a number that would let you figure out what the exact number is if you know the numbers in the floating-point format, but it does not have to be rounded the same as if you took that exact number and rounded it to the number of digits requested. For example, if you use to request 25 digits, some C implementations might produce 17 digits (enough to uniquely distinguish the number) and fill out the rest with zeros.%.25g