Nie dalej jak wczoraj kolega podesłał mi łatkę do mojej biblioteki do słownego zapisu liczb i kwot. Nie zdradzę od kogo, by chronić niewinnego. Grunt, że łatka wyglądała tak:
--- to_words_pl.py (upstream) +++ to_words_pl.py (working copy) @@ -82,7 +82,7 @@ iteration += 1 if unit: result.append(unit) - result.append(u'%d/100' % int(remainder * 100)) + result.append(u'%d/100' % int(round(remainder * 100))) result = ' '.join(result) return result
Zdziwiłem się bardzo, bo zwykłem kwoty odpowiednio zaokrąglać do dwóch miejsc po przecinku. Co się jednak okazało? 0.48
zamieniało się w 0.47
. A dokładniej? W 0.47999999999999998
. Tuś mi, ptaszku.
Patrząc na 0.48
tak naprawdę w głowie widziałem decimal.Decimal('0.48')
. Jak się jednak okazuje, niektórzy próbują operacje finansowe przeprowadzać na liczbach zmiennopozycyjnych. Nie używamy typu float
do operacji finansowych. Dlaczego?
>>> 0.48 0.47999999999999998 >>> 0.82 0.81999999999999995
Do operacji na liczbach o znanej precyzji używamy typu decimal.Decimal
i jego kontrolowanego (i konfigurowalnego!) mechanizmu zaokrąglania:
>>> from decimal import Decimal >>> Decimal('0.48') + Decimal('0.12') Decimal('0.60') >>> vat = Decimal('0.48') * Decimal('0.22') >>> vat.quantize(Decimal('0.01')) Decimal('0.11')