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

Eva*_*ose 11 python sqrt pow

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

这似乎在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.
Run Code Online (Sandbox Code Playgroud)

它看起来像math.pow(math.floor(math.sqrt(99999999999999975425)), 2)回报1e+20.

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

Sha*_*ger 21

你的答案非常复杂.问题实际上不是关于sqrt或者pow,问题是你使用的数字大于浮点数可以精确表示.标准IEEE 64位浮点运算不能表示超过52位的每个整数值(加上一个符号位).

尝试将输入转换为float后退:

>>> int(float(99999999999999975424))
99999999999999967232
>>> int(float(99999999999999975425))
99999999999999983616
Run Code Online (Sandbox Code Playgroud)

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

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


use*_*ica 8

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

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

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

>>> float(99999999999999975425)
9.999999999999998e+19
>>> int(_)
99999999999999983616L
Run Code Online (Sandbox Code Playgroud)

最接近这个数字的平方根的浮点数就是10000000000.0,这就是math.sqrt返回的东西.