python - arredondar - Limitando floats para duas casas decimais



aproximar floating-point (13)

TLDR;)

O problema de arredondamento na entrada / saída foi resolvido pelo Python 2.7.0 e 3.1 definitivamente.

Teste infinito:

import random
for x in iter(random.random, None):           # Verify FOREVER that rounding is fixed :-)
    assert float(repr(x)) == x                # Reversible repr() conversion.
    assert len(repr(round(x, 10))) <= 12      # Smart decimal places in repr() after round.
    if x >= 0.1:                              # Implicit rounding to 12 significant digits
        assert str(x) == repr(round(x, 12))   # by str() is good enough for small errors.
        y = 1000 * x                             # Decimal type is excessive for shopping
        assert str(y) == repr(round(y, 12 - 3))  # in the supermaket with Python 2.7+ :-)

Documentação

Veja as notas de lançamento Python 2.7 - Other Language Altera o quarto parágrafo:

As conversões entre números de ponto flutuante e cadeias agora são arredondadas corretamente na maioria das plataformas. Essas conversões ocorrem em muitos lugares diferentes: str () em carros alegóricos e números complexos; o flutuador e construtores complexos; formatação numérica; serializar e desserializar flutuadores e números complexos usando os módulos marshal , pickle e json ; análise de float e literais imaginários em código Python; e conversão decimal-para-float.

Relacionado a isso, o repr () de um número de ponto flutuante x agora retorna um resultado baseado na string decimal mais curta que tem a garantia de arredondar de volta para x no arredondamento correto (com o modo de arredondamento de arredondamento para a metade). Anteriormente, ele dava uma string baseada no arredondamento de x a 17 dígitos decimais.

O assunto relacionado

Mais informações:: A formatação do float antes do Python 2.7 era semelhante ao atual numpy.float64 . Ambos os tipos usam a mesma precisão dupla de 64 bits IEEE 754 com mantissa de 52 bits. Uma grande diferença é que np.float64.__repr__ é formatado freqüentemente com um número decimal excessivo, de forma que nenhum bit pode ser perdido, mas nenhum número IEEE 754 válido existe entre 13.949999999999999 e 13.950000000000001. O resultado não é bom e a conversão repr(float(number_as_string)) não é reversível. Por outro lado: float.__repr__ é formatado para que cada dígito seja importante; a sequência não tem lacunas e a conversão é reversível. Simplesmente: Se você tiver um número numpy.float64, converta para float normal para ser formatado para humanos, não para processadores numéricos, caso contrário nada mais é necessário com o Python 2.7+.

Eu quero a para ser arredondado para 13,95 .

>>> a
13.949999999999999
>>> round(a, 2)
13.949999999999999

A função round não funciona como eu esperava.


A maioria dos números não pode ser exatamente representada em floats. Se você quiser arredondar o número porque é isso que sua fórmula matemática ou algoritmo requer, então você quer usar round. Se você quer apenas restringir a exibição a uma certa precisão, então nem use round e apenas formate-a como aquela string. (Se você quiser exibi-lo com algum método de arredondamento alternativo, e existem toneladas, então você precisa misturar as duas abordagens.)

>>> "%.2f" % 3.14159
'3.14'
>>> "%.2f" % 13.9499999
'13.95'

E por último, embora talvez mais importante, se você quer matemática exata, então você não quer carros alegóricos em tudo. O exemplo usual é lidar com dinheiro e armazenar centavos como um inteiro.


Como apontado pelo @Matt, o Python 3.6 fornece strings-f , e eles também podem usar parâmetros aninhados :

value = 2.34558
precision = 2
width = 4

print(f'result: {value:{width}.{precision}f}')

que irá mostrar o result: 2.35



Existem novas especificações de formato, Mini-Linguagem de Especificação de Formato de Cadeia :

Você pode fazer o mesmo que:

"{0:.2f}".format(13.949999999999999)

Note que o acima retorna uma string. Para conseguir flutuar, simplesmente enrole com float(...) :

float("{0:.2f}".format(13.949999999999999))

Note que embrulhar com float() não muda nada:

>>> x = 13.949999999999999999
>>> x
13.95
>>> g = float("{0:.2f}".format(x))
>>> g
13.95
>>> x == g
True
>>> h = round(x, 2)
>>> h
13.95
>>> x == h
True

Experimente o código abaixo:

>>> a = 0.99334
>>> a = int((a * 100) + 0.5) / 100.0 # Adding 0.5 rounds it up
>>> print a
0.99

No Python 2.7:

a = 13.949999999999999
output = float("%0.2f"%a)
print output

O round() funciona bem no Python 2.7 ou posterior.

Exemplo:

>>> round(14.22222223, 2)
14.22

Confira a documentação .


O tutorial do Python tem um apêndice chamado Aritmética de Ponto Flutuante: Problemas e Limitações . Leia-o. Ele explica o que está acontecendo e por que o Python está fazendo o melhor. Tem até um exemplo que combina com o seu. Deixe-me citar um pouco:

>>> 0.1
0.10000000000000001

você pode ser tentado a usar a função round() para cortá-la de volta ao dígito único que você espera. Mas isso não faz diferença:

>>> round(0.1, 1)
0.10000000000000001

O problema é que o valor binário de ponto flutuante armazenado para “0.1” já era a melhor aproximação binária possível para 1/10 , então tentar arredondá-lo novamente não pode torná-lo melhor: ele já estava tão bom quanto possível.

Outra conseqüência é que, como 0.1 não é exatamente 1/10 , somando dez valores de 0.1 pode não haver exatamente 1.0 :

>>> sum = 0.0
>>> for i in range(10):
...     sum += 0.1
...
>>> sum
0.99999999999999989

Uma alternativa e solução para seus problemas seria usar o módulo decimal .


Para arredondar um número para uma resolução, a melhor maneira é a seguinte, que pode funcionar com qualquer resolução (0,01 para duas casas decimais ou até mesmo outras etapas):

>>> import numpy as np
>>> value = 13.949999999999999
>>> resolution = 0.01
>>> newValue = int(np.round(value/resolution))*resolution
>>> print newValue
13.95

>>> resolution = 0.5
>>> newValue = int(np.round(value/resolution))*resolution
>>> print newValue
14.0

Usar

print"{:.2f}".format(a)

ao invés de

print"{0:.2f}".format(a)

Porque o último pode levar a erros de saída ao tentar gerar múltiplas variáveis ​​(veja os comentários).


Você está correndo para o antigo problema com números de ponto flutuante que todos os números não podem ser representados. A linha de comando mostra apenas a forma completa de ponto flutuante da memória.

No ponto flutuante, sua versão arredondada é o mesmo número. Como os computadores são binários, eles armazenam números de ponto flutuante como um número inteiro e, em seguida, dividem-no por uma potência de dois, de modo que 13.95 será representado de maneira semelhante a 125650429603636838 / (2 ** 53).

Números de precisão dupla têm 53 bits (16 dígitos) de precisão e flutuadores regulares têm 24 bits (8 dígitos) de precisão. O ponto flutuante no Python usa precisão dupla para armazenar os valores.

Por exemplo,

  >>> 125650429603636838/(2**53)
  13.949999999999999

  >>> 234042163/(2**24)
  13.949999988079071

  >>> a=13.946
  >>> print(a)
  13.946
  >>> print("%.2f" % a)
  13.95
  >>> round(a,2)
  13.949999999999999
  >>> print("%.2f" % round(a,2))
  13.95
  >>> print("{0:.2f}".format(a))
  13.95
  >>> print("{0:.2f}".format(round(a,2)))
  13.95
  >>> print("{0:.15f}".format(round(a,2)))
  13.949999999999999

Se você estiver apenas com duas casas decimais como moeda, terá algumas opções melhores: 1) Use números inteiros e armazene valores em centavos, não em dólares, e divida em 100 para converter em dólares. 2) Ou use um número de ponto fixo como decimal .


orig_float = 232569 / 16000.0

14.5355625

short_float = float("{:.2f}".format(orig_float)) 

14,54





precision