Python 3使用ROUND_HALF_UP上下文舍入一半

时间:2016-11-16 11:54:50

标签: python python-3.x decimal rounding

任何人都可以解释或提出一个解决方法,解释为什么当我在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')

3 个答案:

答案 0 :(得分:10)

请注意,当您致电round时,您获得的是浮动值,而不是Decimalround正在将值强制转换为float,然后根据舍入浮点数的规则对其进行舍入。

如果您在调用ndigits时使用可选的round()参数,则会返回十进制结果,在这种情况下,它将按预期方式进行。

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__方法的行为。 / p>

编辑注意Barry Hurley在评论中说,round()被记录为在没有可选参数和"浮点值"的情况下调用时返回int。如果给出可选参数。 https://docs.python.org/3/library/functions.html#round

答案 1 :(得分:2)

扩展@Duncan的回答,round内置函数在python 2和python 3之间改变,以舍入到最接近的偶数(这是统计中的标准)。

Python2文档:

  

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

Python3文档:

  

...如果两个倍数相等,则向均匀方向进行舍入   选择(例如,圆形(0.5)和圆形(-0.5)都是0,和   圆(1.5)是2)

如果round没有为float提供任何参数,则ndigits会转换为round,因此float的行为与{D}}相同会>>> round(2.5) 2 >>> round(2.500000001) 3 >>> round(3.5) 4 s。

示例(在python3中):

function login()
{
    var username = commandData[0];
    var password = commandData[1];

    $.post('../ajax/login.php', {username: username, password: password}, function(result) {
        if (result.error)
            writeTerminalLine(result.error);
        else
            $.post('../ajax/set-session.php', {key: 'member-id', value: result.member_id});
    });
}

答案 2 :(得分:1)

这是Python 2与3中round的舍入模式与从Python Decimal重新实现C之间的变化组合(参见&# 34; 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__'

因此,在round个对象上调用Decimal将其强制转换为float得到rounded using _Py_double_round;这导致float始终返回,无论是否提供ndigits的值。 decimal2.x的纯Python中实现,而33.2实现({?}}。

In Python 3.3 it got shinny new __round__ method因为它已在C中重新实施:

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

现在,当round被调用时,它会被round(<Decimal_object>)选中。

如果参数PyDec_Round不是,则使用默认上下文(C)映射到ROUND_HALF_EVEN中的ndigits,现在为returns a PyLong(整数)提供,如果是,则在其上调用quantize并返回一个新的舍入Decimal对象。