【问题标题】:Rounding in Python to avoid machine precision errors在 Python 中舍入以避免机器精度错误
【发布时间】:2021-09-16 14:39:50
【问题描述】:

我想检查一个浮点数是否是另一个浮点数的倍数,但遇到了机器精度问题。例如:

t1 = 0.02
factor = 0.01
print(t1%factor==0)

以上输出True,但是

t2 = 0.030000000000000002
print(round(t2,5)%factor==0)

这会输出False。在我的代码中的某些点,我正在检查的数字会产生这些机器精度错误,我想我可以简单地通过四舍五入来解决这个问题(我的代码后面需要 5 个小数位,但如果我只需将其四舍五入到小数点后 2 位)。

任何想法为什么上述检查 round(t2,5)%factor==0 不能按预期工作,以及如何解决?

【问题讨论】:

  • 对我来说,t1%factor == 0 返回 false
  • @ZaidAlShatle 对不起,我的意思是输入 t1=0.02(见编辑),它返回 True。但这揭示了一些非常奇怪的事情。为什么 0.02%0.01==0 返回 True,而 0.03%0.01==0 返回 False?
  • 我认为由于round(t2,5)%factort1%factor 将返回一个浮点数,因此您还需要将其四舍五入为round(round(t2,5)%factor)round(t1%factor)
  • @user696969 的答案也可以是正确的,但是如果差值为 0.4999 则它不起作用,它应该返回 False,但如果你将它四舍五入,它将返回 True。
  • 这是一个很好的解释:stackoverflow.com/a/14763891/3047101

标签: python python-3.x rounding precision


【解决方案1】:

它没有按预期工作,因为检查浮点数是否相等几乎永远不会按预期工作。一个快速的解决方法是使用math.isclose。这也允许您调整容差。请记住,在做算术 mod r 时,r 等于 0,所以你应该检查你是否接近 0 或 r。

import math

t1 = 0.02
factor = 0.01
res = t1 % factor
print(math.isclose(res, 0) or math.isclose(res, factor))

这是非常快速和肮脏的,您需要确保您的容差在这两项检查中正确且等效地工作。

【讨论】:

    【解决方案2】:

    您应该使用十进制模块。 decimal 模块支持快速正确舍入的十进制浮点运算。

    import decimal
    print( decimal.Decimal('0.03') % decimal.Decimal('0.01') == decimal.Decimal('0') )
    

    给:

    True
    

    【讨论】:

      【解决方案3】:

      一般来说,Python 中的浮点数……很混乱,因为没有更好的词。他们可以以非常意想不到的方式行事。 (您可以阅读有关该行为的更多信息here。)

      但是,为了您的目标,更好的方法是:

      t2 = 0.03000003
      factor = 0.01
      precision = 10000  # 4 digits
      print(int(t2*precision)%int(factor*precision)==0)
      

      将数学转换为基于整数的计算可以解决大部分问题。

      【讨论】:

      • 但这并不是 Python 的错。它只是通过委托给硬件来遵循 IEEE 浮点算术规范(这是我对其工作原理的理解)。
      • 我并不反对事实并非如此,但事实是,在其他语言中,花车不会像大多数人期望的那样行事,尤其是对于以前没有遇到过这个问题的人(类似于函数中参数的工作方式,例如def myfunc(newList =[])。)这不是意外行为,而是常见的“陷阱!”。 @KyleParsons 但也许我可以使用更好的措辞。
      • @ZaidAlShatle:它与 Python 完全无关。 JavaScript、C、C++、C#、Java、Haskell、Rust、R、MATLAB 等 - 任何使用硬件支持的浮点的东西 - all 都将具有完全相同的行为。您建议的解决方案不是一个好的解决方案 - 它恰好适用于这个特定示例,但在其他情况下会失败(尝试t2 = 0.57)。 (顺便说一句,t1 在您的代码 sn-p 中未定义。)
      猜你喜欢
      • 2019-09-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-06-11
      • 1970-01-01
      • 2018-03-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多