Limitar los flotantes a dos puntos decimales

Quiero que sea redondeado a 13.95 .

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

La función round no funciona como esperaba.

Se está encontrando en el problema anterior con los números de punto flotante que todos los números no pueden ser representados. La línea de comando solo muestra la forma de punto flotante completa de la memoria.

En coma flotante su versión redondeada es el mismo número. Dado que las computadoras son binarias, almacenan números de punto flotante como un número entero y luego lo dividen por una potencia de dos, por lo que 13.95 se representará de manera similar a 125650429603636838 / (2 ** 53).

Los números de precisión doble tienen 53 bits (16 dígitos) de precisión y los flotadores regulares tienen 24 bits (8 dígitos) de precisión. El punto flotante en Python utiliza doble precisión para almacenar los valores.

Por ejemplo,

  >>> 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 

Si busca solo dos lugares decimales en moneda, entonces tiene un par de mejores opciones: 1) Use números enteros y almacene valores en centavos, no en dólares y luego divida por 100 para convertir a dólares. 2) O use un número de punto fijo como decimal .

Hay nuevas especificaciones de formato, Mini-Idioma de especificación de formato de cadena :

Puedes hacer lo mismo que:

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

Tenga en cuenta que lo anterior devuelve una cadena. Para obtener el flotador, simplemente envuélvalo con float(...) :

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

Tenga en cuenta que envolver con float() no cambia 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 

El round() integrado funciona bien en Python 2.7 o posterior.

Ejemplo:

 >>> round(14.22222223, 2) 14.22 

Echa un vistazo a la documentación .

Siento que el enfoque más simple es usar la función format() .

Por ejemplo:

 a = 13.949999999999999 format(a, '.2f') 13.95 

Esto produce un número flotante como una cadena redondeada a dos puntos decimales.

La mayoría de los números no pueden ser representados exactamente en flotadores. Si desea redondear el número porque eso es lo que requiere su algoritmo o fórmula matemática, entonces desea usar la ronda. Si solo desea restringir la visualización a una cierta precisión, entonces ni siquiera use redondear y simplemente formatee como esa cadena. (Si desea mostrarlo con algún método de redondeo alternativo, y hay toneladas, entonces necesita mezclar los dos enfoques).

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

Y, por último, aunque quizás lo más importante, si quieres cálculos exactos , entonces no quieres flotadores en absoluto. El ejemplo habitual es tratar con dinero y almacenar ‘centavos’ como un entero.

Utilizar

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

en lugar de

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

Debido a que esto último puede llevar a errores de salida al intentar generar múltiples variables (ver comentarios).

Pruebe el siguiente código:

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

Con Python <3 (por ejemplo, 2.6 o 2.7), hay dos formas de hacerlo.

 # Option one older_method_string = "%.9f" % numvar # Option two (note ':' before the '.9f') newer_method_string = "{:.9f}".format(numvar) 

Pero tenga en cuenta que para las versiones de Python superiores a 3 (por ejemplo, 3.2 o 3.3), se prefiere la opción dos.

Para obtener más información sobre la opción dos, sugiero este enlace en el formato de cadena de la documentación de Python .

Y para obtener más información sobre la opción uno, este enlace será suficiente y tiene información sobre las distintas banderas .

Referencia: Convierta el número de punto flotante a una cierta precisión, y luego cópielo a la cadena

Puede modificar el formato de salida:

 >>> a = 13.95 >>> a 13.949999999999999 >>> print "%.2f" % a 13.95 

TLDR;)

El problema de redondeo de entrada / salida se ha resuelto definitivamente en Python 2.7.0 y 3.1 .

Un número correctamente redondeado se puede convertir reversiblemente de un lado a otro:
str -> float() -> repr() -> float() ... o Decimal -> float -> str -> Decimal
Un tipo decimal ya no es necesario para el almacenamiento.

(Naturalmente, puede ser necesario redondear un resultado de sum o resta de números redondeados para eliminar los últimos errores de bit acumulados. Una aritmética decimal explícita puede ser útil, pero una conversión a cadena por str() (es decir, con redondeo a 12 dígitos válidos) es suficiente si no se requiere una precisión extrema o un número extremo de operaciones aritméticas sucesivas.

Prueba infinita :

 import random from decimal import Decimal for x in iter(random.random, None): # Verify FOREVER that rounding is fixed :-) assert float(repr(x)) == x # Reversible repr() conversion. assert float(Decimal(repr(x))) == x 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 a supermaket with Python 2.7+ :-) 

Documentación

Consulte las Notas de la versión Python 2.7 – Otros idiomas cambia el cuarto párrafo:

Las conversiones entre números de punto flotante y cadenas ahora se redondean correctamente en la mayoría de las plataformas. Estas conversiones ocurren en muchos lugares diferentes: str () en flotadores y números complejos; Los constructores de flotadores y complejos; formateo numérico; serialización y deserialización de flotadores y números complejos utilizando los módulos marshal , pickle y json ; análisis de literales flotantes e imaginarios en código Python; y conversión de decimal a flotación.

En relación con esto, el repr () de un número de coma flotante x ahora devuelve un resultado basado en la cadena decimal más corta que se garantiza redondear de nuevo a x en el redondeo correcto (con el modo de redondeo de la mitad a la mitad). Anteriormente, daba una cadena basada en el redondeo de x a 17 dígitos decimales.

El tema relacionado


Más información: el formato de float antes de Python 2.7 fue similar al número actual de numpy.float64 . Ambos tipos utilizan la misma doble precisión IEEE 754 de 64 bits con una mantisa de 52 bits. Una gran diferencia es que np.float64.__repr__ se formatea con frecuencia con un número decimal excesivo para que no se pierda ningún bit, pero no existe un número IEEE 754 válido entre 13.949999999999999 y 13.950000000000001. El resultado no es bueno y la conversión repr(float(number_as_string)) no es reversible con numpy. Por otro lado: float.__repr__ está formateado para que cada dígito sea importante; La secuencia es sin huecos y la conversión es reversible. Simplemente: si tiene un número numpy.float64, conviértalo a flotación normal para que sea formateado para humanos, no para procesadores numéricos, de lo contrario no es necesario nada más con Python 2.7+.

En Python 2.7:

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

Parece que nadie aquí lo ha mencionado todavía, así que déjeme dar un ejemplo en el formato f-string / template-string de Python 3.6, que creo que es muy bonito:

 >>> f'{a:.2f}' 

También funciona bien con ejemplos más largos, con operadores y sin necesidad de parens:

 >>> print(f'Completed in {time.time() - start:.2f}s') 

El tutorial de Python tiene un apéndice llamado Aritmética de punto flotante: problemas y limitaciones . Leerlo Explica lo que está sucediendo y por qué Python está haciendo todo lo posible. Incluso tiene un ejemplo que coincide con el tuyo. Déjame citar un poco:

 >>> 0.1 0.10000000000000001 

puede tener la tentación de usar la función round() para recortarla al dígito que espera. Pero eso no hace ninguna diferencia:

 >>> round(0.1, 1) 0.10000000000000001 

El problema es que el valor binario de punto flotante almacenado para “0.1” ya era la mejor aproximación binaria posible a 1/10 , por lo que tratar de redondearlo de nuevo no puede mejorarlo: ya era tan bueno como es.

Otra consecuencia es que dado que 0.1 no es exactamente 1/10 , sumr diez valores de 0.1 puede no producir exactamente 1.0 , ya sea:

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

Una alternativa y solución a sus problemas sería utilizar el módulo decimal .

Está haciendo exactamente lo que le dijiste que hiciera y está funcionando correctamente. Lea más sobre la confusión de punto flotante y tal vez intente objetos decimales .

Como señaló @Matt, Python 3.6 proporciona f-strings , y también pueden usar parámetros nesteds :

 value = 2.34558 precision = 2 width = 4 print(f'result: {value:{width}.{precision}f}') 

que mostrará el result: 2.35

Puede usar el operador de formato para redondear el valor hasta 2 decimales en python:

 print(format(14.4499923, '.2f')) // output is 14.45 

Para corregir el punto flotante en lenguajes de tipo dynamic como Python y JavaScript, utilizo esta técnica

 # For example: a = 70000 b = 0.14 c = a * b print c # Prints 980.0000000002 # Try to fix c = int(c * 10000)/100000 print c # Prints 980 

También puedes usar decimal como sigue:

 from decimal import * getcontext().prec = 6 Decimal(1) / Decimal(7) # Results in 6 precision -> Decimal('0.142857') getcontext().prec = 28 Decimal(1) / Decimal(7) # Results in 28 precision -> Decimal('0.1428571428571428571428571429') 
 orig_float = 232569 / 16000.0 

14.5355625

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

14.54

 from decimal import Decimal def round_float(v, ndigits=2, rt_str=False): d = Decimal(v) v_str = ("{0:.%sf}" % ndigits).format(round(d, ndigits)) if rt_str: return v_str return Decimal(v_str) 

Resultados:

 Python 3.6.1 (default, Dec 11 2018, 17:41:10) >>> round_float(3.1415926) Decimal('3.14') >>> round_float(3.1445926) Decimal('3.14') >>> round_float(3.1455926) Decimal('3.15') >>> round_float(3.1455926, rt_str=True) '3.15' >>> str(round_float(3.1455926)) '3.15' 

Para redondear un número a una resolución, la mejor manera es la siguiente, que puede funcionar con cualquier resolución (0.01 para dos decimales o incluso otros pasos):

 >>> 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 

Es simple como 1,2,3:

  1. use el módulo decimal para una aritmética de coma flotante decimal rápida y correctamente redondeada:

    d = Decimal (10000000.0000009)

Para lograr el redondeo:

  d.quantize(Decimal('0.01')) 

Resultados con Decimal('10000000.00')

  1. hacer arriba SECO:
  def round_decimal(number, exponent='0.01'): decimal_value = Decimal(number) return decimal_value.quantize(Decimal(exponent)) 

O

  def round_decimal(number, decimal_places=2): decimal_value = Decimal(number) return decimal_value.quantize(Decimal(10) ** -decimal_places) 
  1. upvote esta respuesta 🙂

PD: crítica de otros: el formateo no es redondeado.

El método que utilizo es el de corte de cadena. Es relativamente rápido y simple.

Primero, convierta el flotador en una cadena, elija la longitud que desea que sea.

 float = str(float)[:5] 

En la línea única de arriba, hemos convertido el valor en una cadena, luego mantuvimos la cadena solo en sus primeros cuatro dígitos o caracteres (inclusive).

¡Espero que ayude!