【问题标题】:About python's rounding error and floating-point numbers关于python的舍入误差和浮点数
【发布时间】:2020-08-04 20:58:39
【问题描述】:
>>> 1/3
0.3333333333333333

>>> 1/3+1/3+1/3
1.0 

我不明白为什么这是 1.0。不应该是0.9999999999999999吗? 所以我想出了一个解决方案,python 有一个自动四舍五入的答案,但如果不是,以下结果无法解释......

>>> 1/3+1/3+1/3+1/3+1/3+1/3
1.9999999999999998

>>> (1/3+1/3+1/3)+(1/3+1/3+1/3)
2.0

我认为发生舍入错误是因为尾数中仅使用有限的位数,而指数(以浮点数表示)但 0.9999~~9 也没有超出位数的限制.. 有人能解释一下为什么会出现这样的结果吗?

【问题讨论】:

  • 浮点加法不是关联的。安排计算的不同方式会产生不同的累积舍入误差。
  • @EricPostpischil:有。如果浮点加法是关联的,1/3+1/3+1/3+1/3+1/3+1/3(1/3+1/3+1/3)+(1/3+1/3+1/3) 将是等效的。
  • 也许你把关联性和交换性搞混了。关联性是允许您重新为表达式加上括号的属性。

标签: python floating-point rounding-error


【解决方案1】:

大多数 Python 实现使用二进制浮点格式,最常见的是 IEEE-754 binary64 格式。这种格式有没有个十进制数字。它有 53 个二进制数字。

当这种格式与四舍五入到最近的关系到偶数一起使用时,计算 1/3 得到 0.333333333333333314829616256247390992939472198486328125。默认情况下,您的 Python 实现无法显示完整值;它显示“0.3333333333333333”,这会误导你。

将此数字添加到自身时,结果为 0.66666666666666662965923251249478198587894439697265625。这个结果是准确的;它没有舍入误差。 (也就是说,它没有新的舍入误差;它恰好是 0.333333333333333314829616256247390992939472198486328125 与自身之和。)

当再次添加 0.333333333333333314829616256247390992939472198486328125 时,实数结果不适合 53 位。所以结果必须四舍五入。这种舍入恰好向上舍入,正好产生 1。

当 0.333333333333333314829616256247390992939472198486328125 再次相加时,结果再次不适合,并被四舍五入。这一次,四舍五入恰好是向下的,并产生 1.3333333333333332593184650249895639717578887939453125。

随后的加法产生1.666666666666666518636930049979127943515777587890625然后1.9999999999999997779553950749686919152736663818359375,你的Python实现显示为“1.9999999999999998”。 P>

当你将算术分组为(1/3+1/3+1/3) + (1/3+1/3+1/3)时,每个括号中的项目都会得到1,如上所述,1+1当然是2。

【讨论】:

    【解决方案2】:

    这是 IEEE-754 算法的精妙之处之一。当你写:

    >>> 1/3
    0.3333333333333333
    

    您看到的打印数字是作为1/3 的结果在内部存储的数字的“四舍五入”版本。这正是打印过程中的 Double -> String 转换决定向您展示的内容。但你已经知道了。

    现在您可以问,有没有办法找出其中的区别?是的,使用fractions 模块:

    >>> from fractions import Fraction
    >>> Fraction(1, 3) - Fraction(1/3)
    Fraction(1, 54043195528445952)
    

    啊,这很有趣。所以比实际值略小,相差1 / 54043195528445952。这当然是意料之中的。

    那么,当您将其中的两个“相加”在一起时会发生什么。让我们看看:

    >>> Fraction(2,3) - Fraction(1/3+1/3)
    Fraction(1, 27021597764222976)
    

    同样,您已接近2/3rds,但仍不完全在那里。让我们再做一次加法:

    >>> Fraction(1,1) - Fraction(1/3+1/3+1/3)
    Fraction(0, 1)
    

    宾果!其中 3 个,表示正好是 1

    这是为什么呢?好吧,在每次加法中,您都会得到一个接近您认为答案应该是的数字,但是内部四舍五入会导致结果变成一个接近的数字,这不是您所想的。加上三个你的直觉告诉你的东西,以及内部四舍五入的结果。

    需要强调的是,添加1/3 + 1/3 + 1/3 确实不会产生1;它只生成一个内部值,其最接近的表示为 IEEE-754 双精度浮点值是1。这是一个微妙但重要的区别。希望对您有所帮助!

    【讨论】:

    • 0.3333333333333333 不是 Python 可以向您展示的最接近的。它可以向您显示确切的值,但确切的值需要很多位数。此外,1/3+1/3 与 2/3 的精确值相比,1/3 与 1/3 之间的距离
    【解决方案3】:

    这个问题可能会为浮点错误提供一些答案 Is floating point math broken?

    使用方括号,编译器将加法分解成更小的部分,从而减少不应该存在的浮点继续“累积”浮点错误的可能性。最有可能分成 3 组时,编译器知道总和为 1 并将 1 + 1 加在一起

    【讨论】:

    • 这根本不正确。编译器永远不会知道总和会是 1,也不会按照您告诉它的方式执行任何操作!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-25
    • 2019-03-27
    相关资源
    最近更新 更多