math.sqrt()和math.pow()中的不准确性来自大数字?

时间:2018-01-22 01:21:20

标签: python pow sqrt

如果取一个数字,取其平方根,删除小数,然后将其提高到二次幂,结果应始终小于或等于原始数。

这似乎在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)会返回1e+20

我认为这与我们在python中存储值的方式有关...与浮点运算相关的东西,但我不能具体说明这会如何影响这种情况。

2 个答案:

答案 0 :(得分:21)

你的答案非常复杂。问题不在于sqrtpow,问题是您使用的数字大于浮点数可以精确表示。标准IEEE 64位浮点运算不能表示超过52位的每个整数值(加一个符号位)。

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

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

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

简短版本:float无法准确表示大整数。如果您需要更高的精确度,请使用decimal

答案 1 :(得分:8)

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

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

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

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

与此数字的平方根最接近的浮点数为10000000000.0,这是math.sqrt返回的内容。