O seguinte explica como arredondar números em Python por arredondamento ou arredondamento para um número par. Assume-se que os números são do tipo flutuante de ponto flutuante ou int inteiro.
- função incorporada (por exemplo, em linguagem de programação):
round()
- Redondo decimais a qualquer número de dígitos.
- Redondo números inteiros para qualquer número de dígitos.
- redonda() redonda para um número par, não para um arredondamento comum
- biblioteca padrão
decimal
quantize()
Decimal
Criação de um objecto- Arredondamento de decimais para qualquer número de dígitos e arredondamento para números pares
- Arredondamento de números inteiros para qualquer número de dígitos e arredondamento para números pares
- Definir uma nova função
- Arredondar as casas decimais para qualquer número de dígitos.
- Redondo números inteiros a qualquer número de dígitos
- Nota: Para valores negativos
Note-se que, como mencionado acima, o arredondamento da função integrada não é um arredondamento geral, mas um arredondamento para um número par. Ver abaixo para detalhes.
função incorporada (por exemplo, em linguagem de programação): round()
Round() é fornecido como uma função incorporada. Pode ser utilizado sem importação de quaisquer módulos.
O primeiro argumento é o número original, e o segundo argumento é o número de dígitos (quantos dígitos arredondar para).
Redondo decimais a qualquer número de dígitos.
O seguinte é um exemplo de processamento para o tipo de flutuador de ponto flutuante.
Se o segundo argumento for omitido, é arredondado para um número inteiro. O tipo também se torna um integer int tipo.
f = 123.456 print(round(f)) # 123 print(type(round(f))) # <class 'int'>
Se o segundo argumento for especificado, devolve um tipo de flutuador de ponto flutuante.
Se for especificado um número inteiro positivo, é especificada a casa decimal; se for especificado um número inteiro negativo, é especificada a casa inteira. -1 arredondamentos para o décimo mais próximo, -2 arredondamentos para o centésimo mais próximo, e 0 arredondamentos para um número inteiro (o primeiro lugar), mas devolve um tipo de flutuador, ao contrário de quando omitido.
print(round(f, 1)) # 123.5 print(round(f, 2)) # 123.46 print(round(f, -1)) # 120.0 print(round(f, -2)) # 100.0 print(round(f, 0)) # 123.0 print(type(round(f, 0))) # <class 'float'>
Redondo números inteiros para qualquer número de dígitos.
O seguinte é um exemplo de processamento para o tipo int inteiro.
Se o segundo argumento for omitido, ou se for especificado 0 ou um número inteiro positivo, o valor original é devolvido tal como está. Se for especificado um número inteiro negativo, este é arredondado para o dígito inteiro correspondente. Em ambos os casos, é devolvido um inteiro inteiro do tipo int.
i = 99518 print(round(i)) # 99518 print(round(i, 2)) # 99518 print(round(i, -1)) # 99520 print(round(i, -2)) # 99500 print(round(i, -3)) # 100000
redonda() redonda para um número par, não para um arredondamento comum
Note-se que o arredondamento com a função round() incorporada em Python 3 arredondamentos para um número par, não para um arredondamento geral.
Como escrito na documentação oficial, 0,5 é arredondado para 0, 5 é arredondado para 0, e assim por diante.
print('0.4 =>', round(0.4)) print('0.5 =>', round(0.5)) print('0.6 =>', round(0.6)) # 0.4 => 0 # 0.5 => 0 # 0.6 => 1 print('4 =>', round(4, -1)) print('5 =>', round(5, -1)) print('6 =>', round(6, -1)) # 4 => 0 # 5 => 0 # 6 => 10
A definição de arredondamento para um número par é a seguinte.
Se a fracção for inferior a 0,5, arredondá-la para baixo; se a fracção for superior a 0,5, arredondá-la para cima; se a fracção for exactamente 0,5, arredondá-la para cima até ao número par entre o arredondamento para baixo e o arredondamento para cima.
Rounding – Wikipedia
0,5 nem sempre é truncado.
print('0.5 =>', round(0.5)) print('1.5 =>', round(1.5)) print('2.5 =>', round(2.5)) print('3.5 =>', round(3.5)) print('4.5 =>', round(4.5)) # 0.5 => 0 # 1.5 => 2 # 2.5 => 2 # 3.5 => 4 # 4.5 => 4
Em alguns casos, a definição de arredondamento para um número par nem sequer se aplica ao processamento após duas casas decimais.
print('0.05 =>', round(0.05, 1)) print('0.15 =>', round(0.15, 1)) print('0.25 =>', round(0.25, 1)) print('0.35 =>', round(0.35, 1)) print('0.45 =>', round(0.45, 1)) # 0.05 => 0.1 # 0.15 => 0.1 # 0.25 => 0.2 # 0.35 => 0.3 # 0.45 => 0.5
Isto deve-se ao facto de as casas decimais não poderem ser representadas exactamente como números de pontos flutuantes, tal como indicado na documentação oficial.
O comportamento de redondo() para números de ponto flutuante pode surpreendê-lo:Por exemplo, a ronda(2,675, 2) dar-lhe-á 2,67 em vez de 2,68 como esperado. Isto não é um bug.:Isto resulta do facto de a maioria das casas decimais não poder ser representada exactamente por números de pontos flutuantes.
round() — Built-in Functions — Python 3.10.2 Documentation
Se desejar obter arredondamentos gerais ou arredondamentos precisos de decimais para números pares, pode usar a quantificação padrão de decimais da biblioteca (descrita abaixo) ou definir uma nova função.
Note-se também que o arredondamento() em Python 2 não é um arredondamento para um número par, mas sim um arredondamento.
quantize() da biblioteca padrão decimal
O módulo decimal da biblioteca padrão pode ser utilizado para lidar com números exactos de pontos flutuantes decimais.
Usando o método quantize() do módulo decimal, é possível arredondar números especificando o modo de arredondamento.
- decimal quantize() — Decimal fixed point and floating point arithmetic — Python 3.10.2 Documentation
- Rounding modes — Decimal fixed point and floating point arithmetic — Python 3.10.2 Documentation
Os valores definidos para o arredondamento do argumento do método quantize() têm os seguintes significados, respectivamente.
ROUND_HALF_UP
:Arredondamento geralROUND_HALF_EVEN
:Arredondamento para números pares
O módulo decimal é uma biblioteca padrão, pelo que não é necessária qualquer instalação adicional, mas é necessária a importação.
from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_EVEN
Criação de um objecto Decimal
Decimal() pode ser usado para criar objectos do tipo Decimal.
Se especificar um tipo de flutuador como argumento, pode ver como o valor é realmente tratado.
print(Decimal(0.05)) # 0.05000000000000000277555756156289135105907917022705078125 print(type(Decimal(0.05))) # <class 'decimal.Decimal'>
Como mostrado no exemplo, 0,05 não é tratado como exactamente 0,05. Esta é a razão pela qual a função integrada arredondada() acima descrita é arredondada para um valor diferente do esperado para valores decimais, incluindo 0,05 no exemplo.
Uma vez que 0,5 é metade (-1 potência de 2), pode ser expresso exactamente em notação binária.
print(Decimal(0.5)) # 0.5
Se especificar a string tipo str em vez do tipo float, será tratado como o tipo Decimal do valor exacto.
print(Decimal('0.05')) # 0.05
Arredondamento de decimais para qualquer número de dígitos e arredondamento para números pares
Chamada quantize() de um objecto do tipo Decimal para arredondar o valor.
O primeiro argumento de quantize() é uma cadeia com o mesmo número de dígitos que o número de dígitos que pretende encontrar, como '0,1' ou '0,01'.
Além disso, o argumento ROUNDING especifica o modo de arredondamento; se ROUND_HALF_UP for especificado, é utilizado o arredondamento geral.
f = 123.456 print(Decimal(str(f)).quantize(Decimal('0'), rounding=ROUND_HALF_UP)) # 123 print(Decimal(str(f)).quantize(Decimal('0.1'), rounding=ROUND_HALF_UP)) # 123.5 print(Decimal(str(f)).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)) # 123.46
Ao contrário da função round() integrada, 0,5 é arredondado para 1.
print('0.4 =>', Decimal(str(0.4)).quantize(Decimal('0'), rounding=ROUND_HALF_UP)) print('0.5 =>', Decimal(str(0.5)).quantize(Decimal('0'), rounding=ROUND_HALF_UP)) print('0.6 =>', Decimal(str(0.6)).quantize(Decimal('0'), rounding=ROUND_HALF_UP)) # 0.4 => 0 # 0.5 => 1 # 0.6 => 1
Se o arredondamento do argumento estiver definido para ROUND_HALF_EVEN, o arredondamento é efectuado para números pares como na função integrada round().
Como mencionado acima, se um tipo de flutuador de ponto flutuante for especificado como o argumento de Decimal(), é tratado como um objecto Decimal com um valor igual ao valor real do tipo de flutuador, pelo que o resultado da utilização do método quantize() será diferente do que se espera, tal como a função integrada round().
print('0.05 =>', round(0.05, 1)) print('0.15 =>', round(0.15, 1)) print('0.25 =>', round(0.25, 1)) print('0.35 =>', round(0.35, 1)) print('0.45 =>', round(0.45, 1)) # 0.05 => 0.1 # 0.15 => 0.1 # 0.25 => 0.2 # 0.35 => 0.3 # 0.45 => 0.5 print('0.05 =>', Decimal(0.05).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN)) print('0.15 =>', Decimal(0.15).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN)) print('0.25 =>', Decimal(0.25).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN)) print('0.35 =>', Decimal(0.35).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN)) print('0.45 =>', Decimal(0.45).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN)) # 0.05 => 0.1 # 0.15 => 0.1 # 0.25 => 0.2 # 0.35 => 0.3 # 0.45 => 0.5
Se o argumento de Decimal() for especificado como uma string do tipo str, é tratado como um objecto Decimal exactamente desse valor, pelo que o resultado é o esperado.
print('0.05 =>', Decimal(str(0.05)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN)) print('0.15 =>', Decimal(str(0.15)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN)) print('0.25 =>', Decimal(str(0.25)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN)) print('0.35 =>', Decimal(str(0.35)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN)) print('0.45 =>', Decimal(str(0.45)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN)) # 0.05 => 0.0 # 0.15 => 0.2 # 0.25 => 0.2 # 0.35 => 0.4 # 0.45 => 0.4
Uma vez que 0,5 pode ser correctamente manipulado pelo tipo de flutuador, não há problema em especificar o tipo de flutuador como o argumento de Decimal() quando se arredonda para um número inteiro, mas é mais seguro especificar o tipo de string string quando se arredonda para uma casa decimal.
Por exemplo, 2.675 é na realidade 2.67499…. em tipo flutuante. Portanto, se quiser arredondar para duas casas decimais, deve especificar uma string para Decimal(), caso contrário o resultado será diferente do resultado esperado quer arredondar para o número inteiro mais próximo (ROUND_HALF_UP) ou para um número par (ROUND_HALF_EVEN).
print(Decimal(2.675)) # 2.67499999999999982236431605997495353221893310546875 print(Decimal(2.675).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)) # 2.67 print(Decimal(str(2.675)).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)) # 2.68 print(Decimal(2.675).quantize(Decimal('0.01'), rounding=ROUND_HALF_EVEN)) # 2.67 print(Decimal(str(2.675)).quantize(Decimal('0.01'), rounding=ROUND_HALF_EVEN)) # 2.68
Note que o método quantize() retorna um número de tipo Decimal, por isso se quiser operar com um número de tipo flutuante, é necessário convertê-lo para um tipo flutuante usando a float(), caso contrário, ocorrerá um erro.
d = Decimal('123.456').quantize(Decimal('0.01'), rounding=ROUND_HALF_UP) print(d) # 123.46 print(type(d)) # <class 'decimal.Decimal'> # print(1.2 + d) # TypeError: unsupported operand type(s) for +: 'float' and 'decimal.Decimal' print(1.2 + float(d)) # 124.66
Arredondamento de números inteiros para qualquer número de dígitos e arredondamento para números pares
Se quiser arredondar para um dígito inteiro, especificando algo como '10' como primeiro argumento, não lhe dará o resultado desejado.
i = 99518 print(Decimal(i).quantize(Decimal('10'), rounding=ROUND_HALF_UP)) # 99518
Isto porque quantize() executa arredondamentos de acordo com o expoente do objecto decimal, mas o expoente de Decimal('10') é 0, e não 1.
Pode especificar um expoente arbitrário usando E como fio expoente (por exemplo, '1E1'). O expoente exponente pode ser verificado no método as_tuple.
print(Decimal('10').as_tuple()) # DecimalTuple(sign=0, digits=(1, 0), exponent=0) print(Decimal('1E1').as_tuple()) # DecimalTuple(sign=0, digits=(1,), exponent=1)
Como está, o resultado será em notação exponencial usando E. Se quiser usar notação normal, ou se quiser operar com int() inteiro após o arredondamento, use int() para converter o resultado.
print(Decimal(i).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP)) # 9.952E+4 print(int(Decimal(i).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP))) # 99520 print(int(Decimal(i).quantize(Decimal('1E2'), rounding=ROUND_HALF_UP))) # 99500 print(int(Decimal(i).quantize(Decimal('1E3'), rounding=ROUND_HALF_UP))) # 100000
Se o arredondamento do argumento for definido para ROUND_HALF_UP, o arredondamento geral ocorrerá, por exemplo, 5 será arredondado para 10.
print('4 =>', int(Decimal(4).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP))) print('5 =>', int(Decimal(5).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP))) print('6 =>', int(Decimal(6).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP))) # 4 => 0 # 5 => 10 # 6 => 10
Naturalmente, não há problema se o especificar como um fio.
Definir uma nova função
O método de utilização do módulo decimal é preciso e seguro, mas se não se sentir confortável com a conversão do tipo, pode definir uma nova função para conseguir arredondamentos gerais.
Há muitas maneiras possíveis de o fazer, por exemplo, a seguinte função.
def my_round(val, digit=0): p = 10 ** digit return (val * p * 2 + 1) // 2 / p
Se não precisar de especificar o número de dígitos e arredondar sempre para a primeira casa decimal, pode usar um formulário mais simples.
my_round_int = lambda x: int((x * 2 + 1) // 2)
Se precisar de ser preciso, é mais seguro usar decimal.
O seguinte é apenas para referência.
Arredondar as casas decimais para qualquer número de dígitos.
print(int(my_round(f))) # 123 print(my_round_int(f)) # 123 print(my_round(f, 1)) # 123.5 print(my_round(f, 2)) # 123.46
Ao contrário do arredondamento, 0,5 torna-se 1 por arredondamento geral.
print(int(my_round(0.4))) print(int(my_round(0.5))) print(int(my_round(0.6))) # 0 # 1 # 1
Redondo números inteiros a qualquer número de dígitos
i = 99518 print(int(my_round(i, -1))) # 99520 print(int(my_round(i, -2))) # 99500 print(int(my_round(i, -3))) # 100000
Ao contrário do arredondamento, 5 passa a ser 10, de acordo com o arredondamento comum.
print(int(my_round(4, -1))) print(int(my_round(5, -1))) print(int(my_round(6, -1))) # 0 # 10 # 10
Nota: Para valores negativos
Na função do exemplo acima, -0,5 é arredondado para 0.
print(int(my_round(-0.4))) print(int(my_round(-0.5))) print(int(my_round(-0.6))) # 0 # 0 # -1
Há várias formas de pensar sobre arredondamento para valores negativos, mas se quiser fazer -0,5 em -1, pode modificá-lo da seguinte forma, por exemplo
import math def my_round2(val, digit=0): p = 10 ** digit s = math.copysign(1, val) return (s * val * p * 2 + 1) // 2 / p * s print(int(my_round2(-0.4))) print(int(my_round2(-0.5))) print(int(my_round2(-0.6))) # 0 # -1 # -1