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