【问题标题】:Rounding ** 0.5 and math.sqrt四舍五入 ** 0.5 和 math.sqrt
【发布时间】:2013-06-06 18:37:44
【问题描述】:

在 Python 中,要么是

n**0.5  # or
math.sqrt(n) 

当一个数字是一个完美的正方形时被识别?具体来说,我应该担心当我使用

int(n**0.5)  # instead of
int(n**0.5 + 0.000000001)

由于精度误差,我可能会意外地得到比实际平方根小一的数字?

【问题讨论】:

    标签: python math square-root


    【解决方案1】:

    由于几个答案都建议使用整数运算,我会推荐 gmpy2 库。它提供了检查数字是否为完美幂、计算整数平方根和整数平方根余数的功能。

    >>> import gmpy2
    >>> gmpy2.is_power(9)
    True
    >>> gmpy2.is_power(10)
    False
    >>> gmpy2.isqrt(10)
    mpz(3)
    >>> gmpy2.isqrt_rem(10)
    (mpz(3), mpz(1))
    

    免责声明:我维护 gmpy2。

    【讨论】:

      【解决方案2】:

      是的,你应该担心:

      In [11]: int((100000000000000000000000000000000000**2) ** 0.5)
      Out[11]: 99999999999999996863366107917975552L
      
      In [12]: int(math.sqrt(100000000000000000000000000000000000**2))
      Out[12]: 99999999999999996863366107917975552L
      

      显然在这里添加 0.000000001 也无济于事......

      正如@DSM 指出的,您可以使用decimal 库:

      In [21]: from decimal import Decimal
      
      In [22]: x = Decimal('100000000000000000000000000000000000')
      
      In [23]: (x ** 2).sqrt() == x
      Out[23]: True
      

      对于超过 10**999999999 的数字,只要您检查精度(可配置),它会抛出错误而不是错误答案...

      【讨论】:

      • 将结果四舍五入到float 的精度是导致问题的原因,而不是sqrt 本身的任何缺陷。避免它的唯一方法是使用整数平方根函数。 >>> '%0.0f' % float(100000000000000000000000000000000000) '99999999999999996863366107917975552'
      • 请注意,在使用 Decimal 时,您仍然必须确保精度足够——它使获得正确答案成为可能,而不是自动获得。
      • 你得到了荒谬的数字,因为你平方然后平方根的东西实际上不是 1036,而是最接近它的 double。跨度>
      • @tmyklebu 是的......因此值得担心。
      • @AndyHayden:我的意思是这是一个表示问题,而不是算术问题。
      【解决方案3】:

      **0.5math.sqrt() 都使用浮点运算执行计算。在计算平方根之前将输入转换为浮点数。

      这些计算是否识别输入值何时是一个完美的正方形?

      不,他们没有。浮点算术没有完全平方的概念。

      大整数可能无法表示,因为数字的有效数字多于浮点尾数中可用的数字。因此很容易看出,对于不可表示的输入值,n**0.5 可能不准确。而且您通过添加一个小值提出的修复建议通常不会解决问题。

      如果您的输入是整数,那么您应该考虑使用整数算术进行计算。这最终是处理这个问题的正确方法。

      【讨论】:

      • 你有没有不涉及次正规、下溢或上溢的反例?
      • @tmyklebu 反例。我正在解决是否编码检测完美的正方形。不是因为它使用了没有完全平方概念的浮点运算。
      • 问题的第一句询问识别。第二个询问是否得到错误的答案。所以关于反例的问题是询问您是否可以显示一个完美平方的值,sqrt 不返回确切的平方根。
      • @eric 这很容易展示。您所需要的只是一个不可表示的数字。
      • @tmyklebu 忘记次常态。并且溢出/下溢永远不会出现。我的意思是,对于哪个整数 n,n**0.5 要么上溢,要么下溢。您所需要的只是一个不可代表的数字。
      【解决方案4】:

      您可以在转换为 int 之前使用 round(number, Significant_figures),我不记得在进行浮点到整数转换时是否 python 截断或舍入。

      无论如何,由于 python 使用浮点运算,所有的陷阱都适用。见:
      http://docs.python.org/2/tutorial/floatingpoint.html

      【讨论】:

        【解决方案5】:

        完全平方值没有小数部分,因此您主要担心的是非常大的值,对于这样的值,1 或 2 的差异很重要,这意味着您将需要一个支持如此高的特定数值库精度(正如 DSM 提到的,Decimal 库,自 Python 2.4 以来的标准,应该能够做你想做的事情,因为它支持任意精度。

        http://docs.python.org/library/decimal.html

        【讨论】:

        • +1。 Decimal('10000000000000000000000000000000000000000000000000000000000000000000000').sqrt() Decimal('1.000000000000000000000000000E+35')
        【解决方案6】:

        sqrt 是更容易实现的数学库函数之一,任何质量合理的数学库都会以忠实的舍入(亚 ULP 精度)来实现它。如果输入是一个完美的平方,它的平方根是可表示的(以合理的浮点格式)。在这种情况下,忠实的舍入可以保证结果是准确的。

        这仅处理实际传递给sqrt 的值。是否可以将数字从另一种格式无错误地转换为sqrt 的浮点输入是一个单独的问题。

        【讨论】:

        • 我认为你可以说一些更强有力的东西,即,对于合理的指数,sqrt(x*x) == x 因为(x+ulp/2)*(x+ulp/2),这是sqrt 开始给出错误值的地方,总是超过一个 ulp并且(x-ulp/2)*(x-ulp/2) 比 ulp 更小,除非 x 在 2 的幂的几个 ulp 范围内。
        • @tmyklebu:这似乎是一个使用正确舍入的论点(误差最多为 1/2 ULP)。忠实四舍五入最多允许一个 ULP(不包括)。只要结果不能完全表示,就有可能返回两个值。所以你不能保证sqrt(x*x) == x,除非x*x是准确的。
        • IEEE 不保证正确舍入的sqrt? (我知道 Python 可能没有理由关心 IEEE 所说的内容。但我从未见过数学库得到 sqrt 错误,所以,出于实际目的,我觉得依靠正确舍入的 sqrt 没问题并根据正确舍入的sqrt 提出建议。我认为我的论点可能也适用于忠实舍入的sqrt,但还有更多的边缘情况,特别是在sqrt(2) 周围,需要特别注意。)
        • @tmyklebu:不。IEEE 754-2008 建议正确轮换sqrt。它没有强制要求。而且,正如您所指出的,Python 不保证 IEEE 754。我可以肯定地说,您的论点不适用于忠实的四舍五入。给定任何x,如果sqrt(x*x) 返回x,我可以将其替换为相同的sqrt,除了sqrt(x*x) 返回忠实舍入允许的其他值(不同于x)。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-06-20
        • 2012-01-29
        • 1970-01-01
        • 2020-08-04
        • 2017-11-26
        • 2023-01-12
        • 1970-01-01
        相关资源
        最近更新 更多