[英]Python 3 Decimal rounding half down with ROUND_HALF_UP context
任何人都可以解釋或提出修復為什么當我在 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')
請注意,當您調用round
您將獲得一個浮點值,而不是Decimal
。 round
是將值強制轉換為float,然后根據舍入浮點數的規則舍入該值。
如果在調用round()
時使用可選的ndigits
參數,則會返回Decimal結果,在這種情況下,它將按預期方式舍入。
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)
文件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.
因此行為是一致的,因為沒有參數它會改變結果的類型並將一半__round__
到偶數,但是似乎Decimal
無法記錄其__round__
方法的行為。
編輯注意Barry Hurley在評論中說, round()
被記錄為如果在沒有可選參數的情況下調用則返回int
,如果給出可選參數則返回“浮點值”。 https://docs.python.org/3/library/functions.html#round
擴展@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)
如果沒有為ndigits
給出參數, round
轉換為float
(歸功於ndigits
的答案), round
行為方式與float
s相同。
示例(在python3中):
>>> round(2.5)
2
>>> round(2.500000001)
3
>>> round(3.5)
4
這是舍入模式之間的變化的組合round
在Python 2比3和重新實施的Decimal
從Python來C
(參見在“其它最終大規模變化” 功能為3.3部分PEP 398 )。
對於round
,舍入策略已經改變,可以在Python 3.0中的新功能中看到[另請參閱Python 3.x舍入行為 ]。 另外,Python 3
round
首先嘗試找到為傳遞的對象定義的適當的__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
它首先嘗試將它專門強制轉換為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
這得到了使用四舍五入_Py_double_round
; 這導致float
總是返回, ndigits
是否提供了ndigits
的值。 decimal
是用純Python實現2.x
和(是?)適用於Python 3
直到3.2
。
在Python 3.3
,當它在C
重新實現時, 它有了新的__round__
方法 。
>>> Decimal.__round__
<method '__round__' of 'decimal.Decimal' objects>
而現在,它會在round(<Decimal_object>)
被調用時被round
round(<Decimal_object>)
拾取。
此,映射到PyDec_Round
在C
,現在返回PyLong使用默認上下文((一個整數) ROUND_HALF_EVEN
)如果參數ndigits
不供給,如果它是,來電quantize
它,並返回一個新的圓形Decimal
對象。
德國和瑞士法律要求您將增值稅從 0.005 舍入到 0.01 (“kaufmännisches Runden”) 。 我玩了Decimal
,結果是這樣的:
from decimal import *
class Number(object):
'''generic class for rounding:
Banking: ROUND_HALF_EVEN # default
Germany: ROUND_HALF_UP # for VAT
Switzerland: ROUND_DOWN # for some taxes
ROUND_HALF_UP # for some taxes like VAT
'''
def __init__(self, digits=2, rounding=ROUND_HALF_UP):
getcontext().rounding=rounding
self.base = Decimal(10) ** -digits
def round(self, value, digits=None):
if digits:
base = Decimal(10) ** - digits
else:
base = self.base
return float(Decimal(str(value)).quantize(base))
例子:
>>> a = Number()
>>> a.round(1301.685)
1301.69
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.