Módulo Decimal em Python
Ao trabalhar com números de ponto flutuante (float), frequentemente enfrentamos o problema de obter resultados ligeiramente imprecisos em operações:
number = 0.1 + 0.1 + 0.1
print(number) # 0.30000000000000004Para resolver isso, uma alternativa é usar a função round(), que arredonda o número. Porém, existe uma abordagem mais precisa, que é o uso do módulo decimal.
O principal componente para lidar com números no módulo decimal é a classe Decimal. Para utilizá-la, é necessário criar um objeto desta classe usando o construtor, que recebe uma string representando o número:
from decimal import Decimal
number = Decimal("0.1")Após isso, o objeto Decimal pode ser usado em operações aritméticas:
from decimal import Decimal
number = Decimal("0.1")
number = number + number + number
print(number) # 0.3Em operações com Decimal, é possível utilizar números inteiros:
number = Decimal("0.1")
number = number + 2No entanto, não é permitido misturar números decimais float com Decimal:
number = Decimal("0.1")
number = number + 0.1 # isso levanta uma exceçãoUsando casas decimais adicionais, é possível definir o número de dígitos na parte decimal:
number = Decimal("0.10")
number = 3 * number
print(number) # 0.30A string "0.10" especifica duas casas decimais, mesmo que os últimos dígitos representem zeros. Assim, "0.100" indica três casas decimais.
Arredondamento de Números
Objetos Decimal possuem o método quantize(), que permite arredondar números. Este método recebe como primeiro argumento um objeto Decimal que define o formato de arredondamento:
from decimal import Decimal
number = Decimal("0.444")
number = number.quantize(Decimal("1.00"))
print(number) # 0.44
number = Decimal("0.555678")
print(number.quantize(Decimal("1.00"))) # 0.56
number = Decimal("0.999")
print(number.quantize(Decimal("1.00"))) # 1.00A string "1.00" indica que o arredondamento ocorrerá até duas casas decimais.
Por padrão, o arredondamento é feito usando a constante ROUND_HALF_EVEN, onde o número é arredondado para o par mais próximo se a parte arredondada for igual a 5. Exemplo:
from decimal import Decimal, ROUND_HALF_EVEN
number = Decimal("10.025") # 2 é o par mais próximo
print(number.quantize(Decimal("1.00"), ROUND_HALF_EVEN)) # 10.02
number = Decimal("10.035") # 4 é o par mais próximo
print(number.quantize(Decimal("1.00"), ROUND_HALF_EVEN)) # 10.04A estratégia de arredondamento é passada como segundo parâmetro no quantize.
No primeiro caso, com "10.025", o segundo dígito é 2, um número par, então ele não é arredondado para cima, apesar do próximo número ser 5. No segundo caso, "10.035", o segundo dígito é 3, um número ímpar, então é arredondado para o próximo par, resultando em 10.04.
Esse comportamento pode não ser desejável para todos, e é possível substituí-lo usando uma das seguintes constantes:
ROUND_HALF_UP: arredonda para cima se o próximo dígito for 5 ou maior.ROUND_HALF_DOWN: arredonda para cima se o próximo dígito for maior que 5.number = Decimal("10.026") print(number.quantize(Decimal("1.00"), ROUND_HALF_DOWN)) # 10.03 number = Decimal("10.025") print(number.quantize(Decimal("1.00"), ROUND_HALF_DOWN)) # 10.02ROUND_05UP: arredonda para cima se o próximo dígito for 5 ou maior, mesmo se o dígito atual for zero.number = Decimal("10.005") print(number.quantize(Decimal("1.00"), ROUND_05UP)) # 10.01 number = Decimal("10.025") print(number.quantize(Decimal("1.00"), ROUND_05UP)) # 10.02ROUND_CEILING: arredonda sempre para cima, independente do próximo dígito.number = Decimal("10.021") print(number.quantize(Decimal("1.00"), ROUND_CEILING)) # 10.03 number = Decimal("10.025") print(number.quantize(Decimal("1.00"), ROUND_CEILING)) # 10.03ROUND_FLOOR: arredonda sempre para baixo, independente do próximo dígito.number = Decimal("10.021") print(number.quantize(Decimal("1.00"), ROUND_FLOOR)) # 10.02 number = Decimal("10.025") print(number.quantize(Decimal("1.00"), ROUND_FLOOR)) # 10.02
Documentação oficial