Использование десятичного числа с плавающей запятой устраняет ошибку десятичного представления, позволяя точно представить 0,1. Однако некоторые операции могут все еще вызывать ошибку округления, когда ненулевые цифры превышают фиксированную точность decimal.getcontext().prec
.
Эффект ошибки округления может быть усилен сложением или вычитанием почти компенсирующих величин, что приводит к потере значимости. Дональд Кнут представил два поучительных примера, где округленная арифметика с плавающей точкой с недостаточной точностью приводит к нарушению ассоциативных и распределительных свойств сложения:
>>> from decimal import Decimal, getcontext >>> getcontext().prec = 8 >>> D = Decimal >>> u, v, w = D(11111113), D(-11111111), D('7.51111111') >>> (u + v) + w # Decimal('9.5111111') >>> u + (v + w) # Decimal('10') >>> u, v, w = D(20000), D(-6), D('6.0000003') >>> (u*v) + (u*w) # Decimal('0.01') >>> u * (v+w) # Decimal('0.0060000')
Модуль decimal
позволяет восстановить тождества, достаточно увеличив точность, чтобы избежать потери значимости:
>>> getcontext().prec = 20 >>> u, v, w = Decimal(11111113), Decimal(-11111111), Decimal('7.51111111') >>> (u + v) + w # Decimal('9.51111111') >>> u + (v + w) # Decimal('9.51111111') >>> u, v, w = Decimal(20000), Decimal(-6), Decimal('6.0000003') >>> (u*v) + (u*w) # Decimal('0.0060000') >>> u * (v+w) # Decimal('0.0060000')