math.sqrt() 和 math.pow() 中的不准确性来自哪里?

Where are the inaccuracies in math.sqrt() and math.pow() coming from for large numbers?

如果你取一个数,求它的平方根,去掉小数点,然后求它的二次方,结果应该总是小于或等于原数。

这似乎适用于 python,直到您出于某种原因在 99999999999999975425 上尝试它。

import math

def check(n):
    assert math.pow(math.floor(math.sqrt(n)), 2) <= n

check(99999999999999975424)  # No exception.
check(99999999999999975425)  # Throws AssertionError.

好像是math.pow(math.floor(math.sqrt(99999999999999975425)), 2)returns1e+20.

我认为这与我们在 python 中存储值的方式有关...与浮点运算有关,但我无法具体推断这对这种情况有何影响。

与 Evan Rose(现已删除)的回答声明不同,这不是因为 sqrt 算法中的 epsilon 值。

大多数 math 模块函数将其输入转换为 floatmath.sqrt 就是其中之一。

99999999999999975425 不能表示为浮点数。对于此输入,转换生成一个具有精确数值 9999999999999983616 的浮点数,repr 显示为 9.999999999999998e+19:

>>> float(99999999999999975425)
9.999999999999998e+19
>>> int(_)
99999999999999983616L

最接近这个数的平方根的浮点数是10000000000.0,也就是math.sqrt returns.

问题不在于 sqrtpow,问题在于您使用的数字大于浮点数可以精确表示的数字。标准 IEEE 64 位浮点运算无法表示超过 52 位(加上一个符号位)的每个整数值。

尝试将您的输入转换为 float 然后再转换回来:

>>> int(float(99999999999999975424))
99999999999999967232
>>> int(float(99999999999999975425))
99999999999999983616

如您所见,可表示值跳过了 16384。math.sqrt 中的第一步是转换为 float(C double),此时,您的值增加到足以破坏最终结果。

简短版本:float 无法精确表示大整数。如果您需要更高的精度,请使用 decimal。或者如果你不关心小数部分,从 3.8 开始,你可以 use math.isqrt,它完全以整数 space 工作(所以你永远不会遇到精度损失,只有你期望的舍入损失) ,为您提供您正在寻找的保证,即结果是“满足 a² ≤ n 的最大整数 a”。