2016-11-16 373 views
9

有人能解释或提出为什么当我在Python 3轮的十进制设置为圆形半升的背景下修复后,便将2.5〜2,而在Python二轮正确地3:Python 3的小数四舍五入半下来ROUND_HALF_UP方面

的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') 

回答

9

注意,当你调用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在评论中说round()被记录为返回int如果没有可选参数调用和“浮点值”,如果给定可选参数。 https://docs.python.org/3/library/functions.html#round

+0

感谢@Duncan的解释,尝试理解这些东西总是很好。根据是否指定ndigits,返回不同的_type_似乎很奇怪。 –

+1

作为一个插件,python 3文档声明它委托给'number .__ round __(ndigits)',而'decimal'模块实现了它自己的'__round__'方法 –

+1

@Dunca,它看起来是一个int,而不是float though:“如果ndigits被省略或者是None,它将返回最接近它的输入的整数。” https://docs.python.org/3/library/functions.html#round –

2

在@Duncan的回答中,round内建函数在python 2和python 3之间改变,以四舍五入到最接近的偶数(这是统计学中的常态)。

Python2文档:

...如果两个倍数同样接近,舍入免掉 从0(因此,例如,圆形(0.5)为1.0和圆形(-0.5)是 - 1.0)。

Python3文档:

...如果两个倍数同样接近,舍入朝向甚至 选择(因此,例如,这两个轮(0.5)和轮进行(-0.5)是0和 round(1。5)2)

由于round转换为float如果没有给出参数为ndigits(信用@邓肯的答案),round的行为方式,因为它会为float小号一样。

实例(在python3):

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

感谢@Billy,但是在Python 3中,无论是round(1.5)还是round(2.5)都会给出2,我认为这是由于1.5和2.5浮点数是如何在内部表示的。 Python 2中的返回值为这些返回的浮点数,而ints int Python 3. –

+2

它与内部表示无关。 Python的浮点实现可以精确地代表1.5和2.5 *(即没有像1.1那样的浮点舍入错误)。由于1.5与* 1和* 2的距离刚好相等,所以蟒蛇3轮到最接近的偶数,也就是2.同样地,2.5 *精确地在2和3之间,所以它被舍入到最接近的偶数,又是2。 – Billy

1

这是round在Python 2舍入模式对3和重新执行的Decimal从Python来C之间的变化的组合(参见“的其他最终的大规模变化“Features for 3.3 section PEP 398)。

对于round,舍入策略改变,可以在What's New In Python 3.0中看到[也见Python 3.x rounding behavior]。另外,在Python round3首先尝试为对象定义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;无论是否提供了ndigits的值,都会导致float总是返回。 decimal以纯Python的形式实现,用于2.x和Python(3)的(是?),直到3.2

In Python 3.3 it got shinny new __round__ method因为它是在C重新实现:

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

,现在,它就会通过round当调用round(<Decimal_object>)回升。

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