Pergunta

A documentação para o redondo() A função afirma que você passa um número para ela e as posições após o decimal são arredondadas.Assim é deve fazem isto:

n = 5.59
round(n, 1) # 5.6

Mas, na verdade, a boa e velha estranheza do ponto flutuante surge e você obtém:

5.5999999999999996

Para fins de UI, preciso exibir 5.6.Pesquisei na Internet e encontrei alguns documentação que isso depende da minha implementação do Python.Infelizmente, isso ocorre tanto na minha máquina de desenvolvimento do Windows quanto em todos os servidores Linux que experimentei. Veja aqui também.

Além de criar minha própria biblioteca redonda, há alguma maneira de contornar isso?

Foi útil?

Solução

Não posso evitar a forma como está armazenado, mas pelo menos a formatação funciona corretamente:

'%.1f' % round(n, 1) # Gives you '5.6'

Outras dicas

A formatação funciona corretamente mesmo sem arredondar:

"%.1f" % n

Se você usar o módulo Decimal, poderá aproximar sem usar a função 'round'.Aqui está o que tenho usado para arredondamento, especialmente ao escrever aplicações monetárias:

Decimal(str(16.2)).quantize(Decimal('.01'), rounding=ROUND_UP)

Isso retornará um número decimal que é 16,20.

round(5.59, 1) está funcionando bem.O problema é que 5.6 não pode ser representado exatamente em ponto flutuante binário.

>>> 5.6
5.5999999999999996
>>> 

Como diz Vinko, você pode usar a formatação de string para arredondar para exibição.

Python tem um módulo para aritmética decimal se você precisar disso.

Você obtém '5,6' se fizer isso str(round(n, 1)) em vez de apenas round(n, 1).

Você pode mudar o tipo de dados para um número inteiro:

>>> n = 5.59
>>> int(n * 10) / 10.0
5.5
>>> int(n * 10 + 0.5)
56

E então exiba o número inserindo o separador decimal do código do idioma.

No entanto, A resposta de Jimmy é melhor.

A matemática de ponto flutuante é vulnerável a pequenas, mas irritantes, imprecisões de precisão.Se você puder trabalhar com número inteiro ou ponto fixo, terá precisão garantida.

Dê uma olhada no Módulo decimal

Decimal "é baseado em um modelo de ponto flutuante que foi projetado com pessoas em mente e necessariamente tem um princípio orientador paramount-os computadores devem fornecer uma aritmética que funcione da mesma maneira que a aritmética que as pessoas aprendem na escola". - Trecho da especificação aritmética decimal.

e

Os números decimais podem ser representados exatamente.Por outro lado, números como 1,1 e 2.2 não têm representações exatas no ponto flutuante binário.Os usuários finais normalmente não esperavam que o 1.1 + 2.2 seja exibido como 3.3000000000000003, como acontece com o ponto flutuante binário.

Decimal fornece o tipo de operações que facilitam a gravação de aplicativos que exigem operações de ponto flutuante e também precisa apresentar esses resultados em um formato legível, por exemplo, contabilidade.

É realmente um grande problema.Experimente este código:

print "%.2f" % (round((2*4.4+3*5.6+3*4.4)/8,2),)

Ele exibe 4,85.Então você faz:

print "Media = %.1f" % (round((2*4.4+3*5.6+3*4.4)/8,1),)

e mostra 4,8.Se você fizer cálculos manualmente, a resposta exata será 4,85, mas se você tentar:

print "Media = %.20f" % (round((2*4.4+3*5.6+3*4.4)/8,20),)

você pode ver a verdade:o ponto flutuante é armazenado como a soma finita mais próxima de frações cujos denominadores são potências de dois.

Você pode usar o operador de formato de string %, semelhante ao sprintf.

mystring = "%.2f" % 5.5999

imprimir o otário.

print '%.1f' % 5.59  # returns 5.6

Funciona perfeito

format(5.59, '.1f') # to display
float(format(5.59, '.1f')) #to round

Estou fazendo:

int(round( x , 0))

Nesse caso, primeiro arredondamos corretamente no nível da unidade e depois convertemos para inteiro para evitar a impressão de um float.

então

>>> int(round(5.59,0))
6

Acho que essa resposta funciona melhor do que formatar a string e também faz mais sentido usar a função round.

Código:

x1 = 5.63
x2 = 5.65
print(float('%.2f' % round(x1,1)))  # gives you '5.6'
print(float('%.2f' % round(x2,1)))  # gives you '5.7'

Saída:

5.6
5.7

É aqui que vejo a rodada falhando.E se você quisesse arredondar esses 2 números para uma casa decimal?23.45 23.55 Minha educação foi a que, ao arredondá -las, você deve obter:23.4 23.6 A "regra" é que você deve reunir se o número anterior for estranho, não arredondado se o número anterior fosse par.A função round em python simplesmente trunca o 5.

O problema ocorre apenas quando o último dígito é 5.Por exemplo.0,045 é armazenado internamente como 0,044999999999999...Você poderia simplesmente aumentar o último dígito para 6 e arredondar.Isso lhe dará os resultados desejados.

import re


def custom_round(num, precision=0):
    # Get the type of given number
    type_num = type(num)
    # If the given type is not a valid number type, raise TypeError
    if type_num not in [int, float, Decimal]:
        raise TypeError("type {} doesn't define __round__ method".format(type_num.__name__))
    # If passed number is int, there is no rounding off.
    if type_num == int:
        return num
    # Convert number to string.
    str_num = str(num).lower()
    # We will remove negative context from the number and add it back in the end
    negative_number = False
    if num < 0:
        negative_number = True
        str_num = str_num[1:]
    # If number is in format 1e-12 or 2e+13, we have to convert it to
    # to a string in standard decimal notation.
    if 'e-' in str_num:
        # For 1.23e-7, e_power = 7
        e_power = int(re.findall('e-[0-9]+', str_num)[0][2:])
        # For 1.23e-7, number = 123
        number = ''.join(str_num.split('e-')[0].split('.'))
        zeros = ''
        # Number of zeros = e_power - 1 = 6
        for i in range(e_power - 1):
            zeros = zeros + '0'
        # Scientific notation 1.23e-7 in regular decimal = 0.000000123
        str_num = '0.' + zeros + number
    if 'e+' in str_num:
        # For 1.23e+7, e_power = 7
        e_power = int(re.findall('e\+[0-9]+', str_num)[0][2:])
        # For 1.23e+7, number_characteristic = 1
        # characteristic is number left of decimal point.
        number_characteristic = str_num.split('e+')[0].split('.')[0]
        # For 1.23e+7, number_mantissa = 23
        # mantissa is number right of decimal point.
        number_mantissa = str_num.split('e+')[0].split('.')[1]
        # For 1.23e+7, number = 123
        number = number_characteristic + number_mantissa
        zeros = ''
        # Eg: for this condition = 1.23e+7
        if e_power >= len(number_mantissa):
            # Number of zeros = e_power - mantissa length = 5
            for i in range(e_power - len(number_mantissa)):
                zeros = zeros + '0'
            # Scientific notation 1.23e+7 in regular decimal = 12300000.0
            str_num = number + zeros + '.0'
        # Eg: for this condition = 1.23e+1
        if e_power < len(number_mantissa):
            # In this case, we only need to shift the decimal e_power digits to the right
            # So we just copy the digits from mantissa to characteristic and then remove
            # them from mantissa.
            for i in range(e_power):
                number_characteristic = number_characteristic + number_mantissa[i]
            number_mantissa = number_mantissa[i:]
            # Scientific notation 1.23e+1 in regular decimal = 12.3
            str_num = number_characteristic + '.' + number_mantissa
    # characteristic is number left of decimal point.
    characteristic_part = str_num.split('.')[0]
    # mantissa is number right of decimal point.
    mantissa_part = str_num.split('.')[1]
    # If number is supposed to be rounded to whole number,
    # check first decimal digit. If more than 5, return
    # characteristic + 1 else return characteristic
    if precision == 0:
        if mantissa_part and int(mantissa_part[0]) >= 5:
            return type_num(int(characteristic_part) + 1)
        return type_num(characteristic_part)
    # Get the precision of the given number.
    num_precision = len(mantissa_part)
    # Rounding off is done only if number precision is
    # greater than requested precision
    if num_precision <= precision:
        return num
    # Replace the last '5' with 6 so that rounding off returns desired results
    if str_num[-1] == '5':
        str_num = re.sub('5$', '6', str_num)
    result = round(type_num(str_num), precision)
    # If the number was negative, add negative context back
    if negative_number:
        result = result * -1
    return result

A respeito:

round(n,1)+epsilon
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top