【问题标题】:Python 3 Decimal rounding half down with ROUND_HALF_UP contextPython 3 Decimal 使用 ROUND_HALF_UP 上下文舍入一半
【发布时间】:2017-03-30 14:48:48
【问题描述】:

任何人都可以解释或提出解决方案,为什么当我在 Python 3 中将小数四舍五入且上下文设置为四舍五入时,它会将 2.5 舍入为 2,而在 Python 2 中它正确地四舍五入为 3:

Python 3.4.3 和 3.5.2:

>>> import decimal
>>> context = decimal.getcontext()
>>> context.rounding = decimal.ROUND_HALF_UP
>>> round(decimal.Decimal('2.5'))
2
>>> decimal.Decimal('2.5').__round__()
2
>>> decimal.Decimal('2.5').quantize(decimal.Decimal('1'), rounding=decimal.ROUND_HALF_UP)
Decimal('3')

Python 2.7.6:

>>> import decimal
>>> context = decimal.getcontext()
>>> context.rounding = decimal.ROUND_HALF_UP
>>> round(decimal.Decimal('2.5'))
3.0
>>> decimal.Decimal('2.5').quantize(decimal.Decimal('1'), rounding=decimal.ROUND_HALF_UP)
Decimal('3')

【问题讨论】:

    标签: python python-3.x decimal rounding


    【解决方案1】:

    请注意,当您调用round 时,您将得到一个浮点值,而不是Decimalround 将值强制为浮点数,然后根据舍入浮点数的规则对其进行舍入。

    如果您在调用round() 时使用可选的ndigits 参数,您将得到一个十进制结果,在这种情况下,它将按照您的预期进行舍入。

    Python 3.4.1 (default, Sep 24 2015, 20:41:10) 
    [GCC 4.9.2 20150212 (Red Hat 4.9.2-6)] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import decimal
    >>> context = decimal.getcontext()
    >>> context.rounding = decimal.ROUND_HALF_UP
    >>> round(decimal.Decimal('2.5'), 0)
    Decimal('3')
    

    我还没有找到在哪里记录 round(someDecimal) 返回一个 int 但 round(someDecimal, ndigits) 返回一个小数,但这似乎是 Python 3.3 及更高版本中发生的情况。在 Python 2.7 中,当您调用 round() 时总是会返回一个浮点数,但 Python 3.3 改进了 Decimal 与 Python 内置函数的集成。

    如评论中所述,round() 代表Decimal.__round__(),这确实显示了相同的行为:

    >>> Decimal('2.5').__round__()
    2
    >>> Decimal('2.5').__round__(0)
    Decimal('3')
    

    我注意到Fraction 的文档说:

    __round__()
    __round__(ndigits)
    The first version returns the nearest int to self, rounding half to even.
    The second version rounds self to the nearest multiple of Fraction(1, 10**ndigits)
    (logically, if ndigits is negative), again rounding half toward even. 
    This method can also be accessed through the round() function.
    

    因此行为是一致的,因为没有参数它会更改结果的类型并将一半舍入为偶数,但似乎Decimal 未能记录其__round__ 方法的行为。

    编辑要注意,正如 Barry Hurley 在 cmets 中所说,round() 记录为返回 int 如果在没有可选参数的情况下调用,如果给定可选参数则返回“浮点值”。 https://docs.python.org/3/library/functions.html#round

    【讨论】:

    • 感谢@Duncan 的解释,尝试理解这些东西总是好的。根据您是否指定 ndigits,返回不同的 type 似乎很奇怪。
    • 作为一个附加组件,python 3 文档声明它委托给number.__round__(ndigits) 并且decimal 模块实现了它自己的__round__ 方法
    • @Dunca,它看起来是一个 int,而不是 float:“如果 ndigits 被省略或为 None,它会返回最接近其输入的整数。” docs.python.org/3/library/functions.html#round
    • @BarryHurley 你是对的,所以这是我没有发现的文档链接(尽管“浮点值”可能不是表达它的最佳方式)。我已更新我的答案以包含此内容。
    【解决方案2】:

    扩展@Duncan 的答案,round 内置函数在 python 2 和 python 3 之间更改为四舍五入到最接近的偶数(这是统计数据的标准)。

    Python2 文档:

    ...如果两个倍数相等,则舍入 从 0 开始(例如,round(0.5) 是 1.0,而 round(-0.5) 是 -1.0)。

    Python3 文档:

    ...如果两个倍数相等,则舍入到偶数 选择(例如,round(0.5) 和 round(-0.5) 都是 0,并且 round(1.5) 是 2)

    由于round 转换为float 如果没有为ndigits 提供参数(归功于@Duncan 的回答),round 的行为方式与floats 的行为方式相同。

    示例(在 python3 中):

    >>> round(2.5)
    2
    >>> round(2.500000001)
    3
    >>> round(3.5)
    4
    

    【讨论】:

    • 感谢@Billy,但在 Python 3 中,round(1.5) 和 round(2.5) 都给出了 2,我认为这是由于 1.5 和 2.5 浮点数在内部是如何表示的。 Python 2 中的 round 为这些返回浮点数,与 Python 3 中的 int int 相比。
    • 它与内部表示无关。 Python 的浮点实现可以精确地表示 1.5 和 2.5(即没有像数字 1.1 那样的浮点舍入错误)。由于 1.5 与 1 和 2 的距离恰好相等,python 3 舍入到最接近的偶数,即 2。类似地,2.5 恰好在 2 和 3 之间,所以它被四舍五入到最接近的偶数,也就是 2。
    【解决方案3】:

    这是 Python 2 vs 3 中 round 的舍入模式和从 Python 到 C 重新实现 Decimal 之间的变化的组合(参见“其他最终大规模变化” Features for 3.3 section PEP 398)。

    对于round,舍入策略已更改,如What's New In Python 3.0 [另见Python 3.x rounding behavior]。此外,Python 中的round 3 首先尝试为传递的对象定义find an appropriate __round__ 方法:

    >>> round('1')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: type str doesn't define __round__ method
    

    在 Python 2.x 中,它首先尝试coerce it specifically to a float,然后对其进行舍入:

    >>> round('1')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: a float is required
    

    对于Decimal,在Python中2,实现甚至缺少__round__方法被调用:

    >>> Decimal.__round__
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: type object 'Decimal' has no attribute '__round__'
    

    因此,在 Decimal 对象上调用 round 会将其强制转换为 float,从而得到 rounded using _Py_double_round;这导致float 总是被返回,无论是否提供了ndigits 的值。 decimal 是在纯 Python 中实现的,对于 2.x 和(是?)对于 Python 3 直到 3.2

    In Python 3.3 it got shinny new __round__ methodC 中重新实现:

    >>> Decimal.__round__
    <method '__round__' of 'decimal.Decimal' objects>
    

    现在,当round(&lt;Decimal_object&gt;) 被调用时,它会被round 拾取。

    如果未提供参数 ndigits,则此,映射到 C 中的 PyDec_Round,现在使用默认上下文 (ROUND_HALF_EVEN) 的 returns a PyLong(整数),如果是,则调用 @987654329 @ 并返回一个新的圆形 Decimal 对象。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-28
      相关资源
      最近更新 更多