Обратите внимание, что трюк ceil(num * 100) / 100
будет зависать на некоторых вырожденных входах, таких как 1e308.Это может случаться не часто, но я могу сказать, что это стоило мне пару дней.Чтобы избежать этого, «было бы неплохо, если бы» ceil()
и floor()
принимали аргумент с десятичными знаками, как это делает round()
... Между тем, любой знает чистую альтернативу, которая не потерпит крахна входах, как это?У меня были некоторые надежды на пакет decimal
, но он, похоже, тоже умирает:
>>> from math import ceil
>>> from decimal import Decimal, ROUND_DOWN, ROUND_UP
>>> num = 0.1111111111000
>>> ceil(num * 100) / 100
0.12
>>> float(Decimal(num).quantize(Decimal('.01'), rounding=ROUND_UP))
0.12
>>> num = 1e308
>>> ceil(num * 100) / 100
Traceback (most recent call last):
File "<string>", line 301, in runcode
File "<interactive input>", line 1, in <module>
OverflowError: cannot convert float infinity to integer
>>> float(Decimal(num).quantize(Decimal('.01'), rounding=ROUND_UP))
Traceback (most recent call last):
File "<string>", line 301, in runcode
File "<interactive input>", line 1, in <module>
decimal.InvalidOperation: [<class 'decimal.InvalidOperation'>]
Конечно, можно сказать, что сбой является единственным нормальным поведением на таких входах, но яЯ бы сказал, что проблема не в округлении, а в умножении (именно поэтому, например, 1e306 не дает сбоя), а более чистая реализация fn с округлением-вверх позволяет избежать взлома умножения.