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.30000000000000004
Para 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.3
Em operações com Decimal
, é possível utilizar números inteiros:
number = Decimal("0.1")
number = number + 2
No entanto, não é permitido misturar números decimais float
com Decimal
:
number = Decimal("0.1")
number = number + 0.1 # isso levanta uma exceção
Usando 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.30
A 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.00
A 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.04
A 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.02
ROUND_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.02
ROUND_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.03
ROUND_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