【问题标题】:Where are the inaccuracies in math.sqrt() and math.pow() coming from for large numbers? [duplicate]对于大数,math.sqrt() 和 math.pow() 的不准确性来自哪里? [复制]
【发布时间】:2018-01-22 01:21:20
【问题描述】:

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

这在 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 中存储值的方式有关......与浮点运算有关,但我无法具体解释这对这种情况有何影响。

【问题讨论】:

  • 请注意,尽管4503599761588224 及其平方根的下限(67108864)都足够小,但在大多数机器上您也会得到 n = 4503599761588224 的例外情况在 IEEE 754 binary64 浮点中精确表示。因此,它比仅使用大于浮点可以表示的数字更微妙。
  • (顺便说一句,我认为这里的副本不合适。对于该副本的答案中未包含的特定问题,还有很多有用的内容可以说。)跨度>

标签: python pow sqrt


【解决方案1】:

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

尝试将您的输入转换为 float 并再次返回:

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

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

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

【讨论】:

  • 我建议您添加“64 位浮点数”以指定更大的浮点数将处理更大的值。
  • @progo:我记下了。与基本 Python 无关,其中只有 float(64 位 C double),但是是的,如果您可以访问其他浮点类型(例如,通过 gmpy2.mpfr 指定 precision),您可以执行更多精确的计算。或者您使用decimal 模块来获得固定的十进制数字精度。
  • 明白。一般来说,人们对浮点数和小数有很多误解,我认为每一点清晰都会有所帮助
【解决方案2】:

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

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

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

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

最接近该数字平方根的浮点数是10000000000.0,这就是math.sqrt 返回的值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-20
    • 1970-01-01
    • 2022-01-01
    • 2010-09-21
    相关资源
    最近更新 更多