Solucionado (ver solução)
Solucionado
(ver solução)
3
respostas

Dúvida e alternativas ao ROUND_HALF_UP

Dúvidas sobre a estratégia ROUND_HALF_UP:

  • Por que essa estratégia faz os números negativos ficarem ainda menores? Exemplo: -1,55 => -1,6
  • Por que essa estratégia não arredonda todos os decimais em vez de só o último? Na real, essa é uma pergunta para todas as estratégias de arredondamento. Exemplo: 1,245 => 1.2

Quais alternativas existem?

Eu gostaria de usar algo como o ROUND_HALF_UP, mas que:

  • Considerasse os valores de todas as casas decimais
  • Sempre tivesse tendência ao infinito positivo
  • Só tomasse ação quando tivesse um valor na casa decimal acima de 5

Eu criei o código que preciso, mas eu gostaria de saber se já existe alguma biblioteca pronta com esse modo, ou se o próprio Python já tem alguma solução pronta.

Segue o código que fiz. Os valores da função round_half_up (os da esquerda), são os que eu quero receber:

from decimal import Decimal, ROUND_HALF_DOWN, ROUND_DOWN, ROUND_HALF_UP


def to_decimal(value):
    return Decimal(str(value))


def round_to(value, decimals=0, strategy=ROUND_HALF_UP):
    precision = Decimal('1.' + '0' * decimals)
    return to_decimal(value).quantize(precision, strategy)


def round_half_up(value, decimals=0):
    value = to_decimal(value)
    if value > 0:
        value = _aux_round_up(value, decimals)
    return round_to(value, decimals, ROUND_HALF_DOWN)


def _aux_round_up(value, decimals):
    multiplier = 10 ** decimals
    multiplied_value = (value * multiplier).normalize()

    _, extra_decimals = str(multiplied_value).split('.')
    extra_decimals = len(extra_decimals)

    aux_value = '0.' + ('5' * extra_decimals)
    aux_value = Decimal(aux_value)

    return (multiplied_value + aux_value).quantize(Decimal('1.'), ROUND_DOWN) / multiplier


print('1 decimal')
print(round_half_up(1.23, 1), round_to(1.23, 1))   # 1.2 | 1.2
print(round_half_up(1.25, 1), round_to(1.25, 1))   # 1.3 | 1.3
print(round_half_up(1.235, 1), round_to(1.235, 1)) # 1.2 | 1.2
print(round_half_up(1.245, 1), round_to(1.245, 1)) # 1.3 | 1.2

print('2 decimals')
print(round_half_up(1.234, 2), round_to(1.234, 2))   # 1.23 | 1.23
print(round_half_up(1.235, 2), round_to(1.235, 2))   # 1.24 | 1.24
print(round_half_up(1.2344, 2), round_to(1.2344, 2)) # 1.23 | 1.23
print(round_half_up(1.2345, 2), round_to(1.2345, 2)) # 1.24 | 1.23

print('negative number, 1 decimal')
print(round_half_up(-1.23, 1), round_to(-1.23, 1))   # -1.2 | -1.2
print(round_half_up(-1.25, 1), round_to(-1.25, 1))   # -1.2 | -1.3
print(round_half_up(-1.235, 1), round_to(-1.235, 1)) # -1.2 | -1.2
print(round_half_up(-1.245, 1), round_to(-1.245, 1)) # -1.2 | -1.2

print('negative number, 2 decimals')
print(round_half_up(-1.234, 2), round_to(-1.234, 2))   # -1.23 | -1.23
print(round_half_up(-1.235, 2), round_to(-1.235, 2))   # -1.23 | -1.24
print(round_half_up(-1.2344, 2), round_to(-1.2344, 2)) # -1.23 | -1.23
print(round_half_up(-1.2345, 2), round_to(-1.2345, 2)) # -1.23 | -1.23
3 respostas

Por que essa estratégia não arredonda todos os decimais em vez de só o último? Na real, essa é uma pergunta para todas as estratégias de arredondamento. Exemplo: 1,245 => 1.2

Uma pessoa já me respondeu pessoalmente. Como o 4 é menor que 5, então não tem mesmo o que arredondar. Achei que tinha que ir arredondando de 1 em 1.

solução!

Bom dia Klaus, tudo bem? Espero que sim!

Desculpe pela demora em retornar.

  • Por que essa estratégia faz os números negativos ficarem ainda menores? Exemplo: -1,55 => -1,6

O arredondamento através do ROUND_HALF_UP irá retornar um valor arredondado mais distante do 0 possível.

Sendo assim quando o número o qual você quer arredondar for negativo, e a casa decimal que será arredondada for um número maior ou igual a 5, o número será arredondado para um número menor ( ou um número mais negativo, ou mais à esquerda na reta real), porque estará se distanciando mais do número 0, que é a origem da reta real.

  • Por que essa estratégia não arredonda todos os decimais em vez de só o último? Na real, essa é uma pergunta para todas as estratégias de arredondamento. Exemplo: 1,245 => 1.2

A ideia do arredondamento é buscar o valor mais próximo com o número de casas decimais que você escolheu. Se foi escolhido apenas uma casa decimal, você não pode usar o último número 5 para realizar o arredondamento.

Se fossem duas casas decimais, o 5 seria usado e o resultado do arredondamento usando a estratégia do ROUND_HALF_UP seria 1.25, porque o 1.25 é o número mais próximo com duas casas decimais usando a estratégia. Mas para 1 casa decimal apenas, você não pode afirmar que 1.3 é o número mais próximo de 1.245, porque tem que levar em consideração apenas a próxima casa decimal, que no caso tem o valor 4. Ela tem mais peso do que a casa dos milésimos, um valor 10 vezes maior na realidade.

Portanto para atingir seu objetivo:

  • Considerasse os valores de todas as casas decimais
  • Sempre tivesse tendência ao infinito positivo
  • Só tomasse ação quando tivesse um valor na casa decimal acima de 5

A primeira parte não seria muito válida, porque o seu arredondamento traria um número muito distante do valor mais próximo.

A segunda parte, basta você utilizar o ROUND_HALF_DOWN para os números negativos, que é uma estratégia que busca levar os números para próximo de 0, que é o caso contrário ao ROUND_HALF_UP.

A terceira parte já é levada em consideração usando as duas estratégias ROUND_HALF_DOWN e ROUND_HALF_UP.

Estou à disposição. Bons estudos!

Opa. Sem problemas. Eu acabei abandonando a dúvida também hehe.

E sim, acabei seguindo a estratégia que falou mesmo, usando o ROUND_HALF_DOWN e o ROUND_HALF_UP. Depois de um bom tempo pensando sobre nomenclaturas e as razões filosóficas do nome delas, consegui entender que o termo UP realmente faz sentido pra se distanciar do zero.

Quer mergulhar em tecnologia e aprendizagem?

Receba a newsletter que o nosso CEO escreve pessoalmente, com insights do mercado de trabalho, ciência e desenvolvimento de software