Limitar flotantes a dos decimales

1692

Quiero aser redondeado a 13.95 .

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

La roundfunción no funciona como esperaba.

ShadowRanger
fuente
66
Hmm ... ¿Estás tratando de representar la moneda? Si es así, no deberías usar flotadores por dólares. Probablemente podría usar flotadores para centavos, o cualquiera que sea la unidad de moneda común más pequeña que intenta modelar, pero la mejor práctica es usar una representación decimal, como sugirió HUAGHAGUAH en su respuesta.
SingleNegationElimination
63
Es importante no representar la moneda en flotante. Las carrozas no son precisas. Pero las cantidades de centavo o centavo son enteros. Por lo tanto, los enteros son la forma correcta de representar la moneda.
Davoud Taghawi-Nejad
2
@ DavoudTaghawi-Nejad o más al punto ... El tipo decimal
Básico
17
Probablemente llegue demasiado tarde aquí, pero quería preguntar, ¿los desarrolladores de Python han resuelto este problema? Porque cuando hago la ronda (13.949999999999999, 2), simplemente obtengo 13.95. Lo he probado en Python 2.7.6, así como en 3.4. Funciona. No estoy seguro si 2.7 incluso estuvo allí en 2009. ¿Quizás es una cosa de Python 2.5?
bad_keypoints

Respuestas:

1690

Se encuentra con el viejo problema con los números de coma flotante que no todos los números pueden representarse exactamente. La línea de comando solo le muestra el formulario de coma flotante completo de la memoria.

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

Los números de doble precisión tienen 53 bits (16 dígitos) de precisión y los flotantes regulares tienen 24 bits (8 dígitos) de precisión. El tipo de coma flotante en Python usa 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("{:.2f}".format(a))
13.95
>>> print("{:.2f}".format(round(a, 2)))
13.95
>>> print("{:.15f}".format(round(a, 2)))
13.949999999999999

Si solo busca dos decimales (para mostrar un valor de moneda, por ejemplo), entonces tiene un par de opciones mejores:

  1. Use enteros y almacene valores en centavos, no en dólares, y luego divídalos entre 100 para convertirlos en dólares.
  2. O use un número de punto fijo como decimal .
Rex Logan
fuente
27
@ Christian Hay una diferencia fundamental entre el valor almacenado y cómo mostrar ese valor. Formatear la salida debería permitirle agregar relleno según sea necesario, así como agregar separadores de coma, etc.
Básico
22
Vale la pena mencionar que "%.2f" % round(a,2)puede incluir no solo en printf, sino también en cosas comostr()
andilabs
20
¿Por qué la gente siempre asume moneda en el redondeo de punto flotante? a veces solo quieres trabajar con menos precisión.
worc
99
@radtek: Debe comprender que el valor binario (de tipo float) es solo la aproximación más cercana disponible del número decimal (con el que está familiarizado como ser humano). No existe tal valor binario (finitamente representable) como 0.245. Simplemente no existe, y matemáticamente no puede existir. El valor binario más cercano a 0.245 es ligeramente menor que 0.245, por lo que naturalmente se redondea hacia abajo. Del mismo modo, no existe el valor 0.225 en binario, pero el valor binario más cercano a 0.225 es ligeramente mayor que 0.225, por lo que naturalmente se redondea.
John Y
12
@radtek: Literalmente pediste una explicación. La solución más sencilla es, de hecho Decimal, usar , y esa fue una de las soluciones presentadas en esta respuesta. El otro era convertir sus cantidades a enteros y usar aritmética de enteros. Ambos enfoques también aparecieron en otras respuestas y comentarios.
John Y
586

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

Puedes hacer lo mismo que:

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

Nota 1: lo anterior devuelve una cadena. Para obtener el flotador, simplemente envuelva con float(...):

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

Nota 2: envolver con float()no cambia nada:

>>> x = 13.949999999999999999
>>> x
13.95
>>> g = float("{:.2f}".format(x))
>>> g
13.95
>>> x == g
True
>>> h = round(x, 2)
>>> h
13.95
>>> x == h
True
Xolve
fuente
17
para agregar comas también puede '{0:,.2f}'.format(1333.949999999)que imprime '1,333.95'.
Stephen Blum
@ OnurYıldırım: sí, pero puedes envolverlo con float(); float("{0:.2f}".format(13.9499999))
Jossef Harush
55
@JossefHarush puedes envolverlo con float (), pero no has ganado nada. Ahora tienes un flotador de nuevo, con la misma imprecisión. 13.9499999999999 y 13.95 son el mismo flotador.
Ned Batchelder
44
@NedBatchelder: estoy de acuerdo en que son iguales, pero esto limita el flotador a dos puntos decimales :)
Jossef Harush
8
Por cierto, desde Python 3.6 podemos usar cadenas f:f"Result is {result:.2f}"
Andrey Semakin
289

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

Ejemplo:

>>> round(14.22222223, 2)
14.22

Echa un vistazo a la documentación .

chribsen
fuente
1
Entonces, ¿debo entender que este es un error de Python 2.7? ¿Por qué una función tan fundamental produciría resultados diferentes de v 2.7 a v 3?
MikeM
pero round(2.16, 1)dará 2.2por qué pitón sólo ofrece una truncatefunc
jiamo
Por ejemplo, si intenta redondear el valor 2.675 a dos decimales, obtendrá este >>> round(2.675, 2) 2.67 docs.python.org/2/tutorial/floatingpoint.html
danger89
44
De la página de documentación de Python 3:Note The behavior of round() for floats can be surprising: for example, round(2.675, 2) gives 2.67 instead of the expected 2.68. This is not a bug: it’s a result of the fact that most decimal fractions can’t be represented exactly as a float.
Richard Dally
Tenga en cuenta que si intenta utilizar este método para imprimir un número como 1.00000, solo imprimirá 1.0, independientemente de cuántos puntos decimales especifique.
Josh Correia
142

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

Por ejemplo:

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

13.95

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

Grant Zukowski
fuente
97

Utilizar

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

en vez de

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

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

Alexey Antonenko
fuente
2
Esto no tiene sentido. Las dos declaraciones dadas se comportan de manera idéntica en Python 2.7, y solo la segunda declaración es válida en Python 2.6. (Ninguna declaración es válida en Python 3 o Python <2.6.) La primera forma no tiene ninguna ventaja además de la brevedad.
Mark Dickinson el
1
Quiero decir, imprimir "{0: .2f} {0: .2f}". Format (a, b) conducirá a un error en la salida; generará el valor 'a' dos veces. Mientras imprime "{:. 2f} {: .2f}". Format (a, b) mostrará los valores 'a' y 'b'.
Alexey Antonenko
2
Para Python 3, solo necesita agregar corchetes de impresión (...). Y dentro de ellos todo lo que escribí es correcto.
Alexey Antonenko
"Quiero decir, imprimir" {0: .2f} {0: .2f} ". Formato (a, b) dará lugar a un error en la salida". Ah Bueno, esa es una declaración bastante diferente. ¿Quizás deberías editar tu respuesta? (¿Qué significa "elevar error" en la respuesta actual, por ejemplo? ¿Puede dar un ejemplo de un caso en el que la segunda declaración genera una excepción pero la primera no?)
Mark Dickinson
3
Sería después de imprimir ("{0: .2f} {1: .2f}". Format (a, b)) si tiene dos variables
Hovo
95

La mayoría de los números no pueden representarse exactamente en carrozas. Si desea redondear el número porque eso es lo que requiere su fórmula matemática o algoritmo, entonces desea usar redondear. Si solo desea restringir la visualización a una cierta precisión, 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 matemáticas exactas , entonces no quieres flotadores en absoluto. El ejemplo habitual es tratar con dinero y almacenar 'centavos' como un número entero.


fuente
68

Prueba 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
ax003d
fuente
Pero tenga cuidado, el valor de a sigue siendo un flotador impreciso. Eche un vistazo aquí: repl.it/LJs (haga clic en "Ejecutar sesión" en la parte superior de la sección Derecha).
Lifebalance
3
Si sigue este enfoque, debe agregar un 0.5 para una representación más precisa. int (a * 100 + 0.5) / 100.0; Usar math.ceil es otra opción.
Arhuaco
3
@ShashankSawant: Bueno, para empezar, la respuesta presentada no se redondea, se trunca. La sugerencia de agregar la mitad al final se redondeará, pero entonces no hay ningún beneficio en hacerlo simplemente usando la roundfunción en primer lugar. Por otro lado, debido a que esta solución todavía usa coma flotante, el problema original del OP permanece, incluso para la versión "corregida" de esta "solución".
John Y
3
-1, esto es solo una reimplementación innecesaria de la roundfunción (que se utilizó en la pregunta).
Interjay
44
@interjay, que es necesario si round()no funciona como el OP mencionado.
Pithikos
57

TLDR;)

El problema de redondeo de entrada / salida ha sido resuelto definitivamente por Python 2.7.0 y 3.1 .

Un número correctamente redondeado puede convertirse 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 la suma o resta de números redondeados para eliminar los errores acumulados del último bit. Una aritmética decimal explícita puede ser útil, pero una conversión a cadena por str()(es decir, redondeando a 12 dígitos válidos) ) es lo suficientemente bueno generalmente si no se requiere una precisión extrema o no se requiere 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 cambios de idioma en 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 flotantes y números complejos; el flotador y los constructores complejos; formato numérico; serializar y de-serializar los flotadores y los números complejos utilizando los marshal, pickley jsonmódulos; análisis de flotantes y literales imaginarios en código Python; y conversión de decimal a flotante.

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 que redondea a x bajo el redondeo correcto (con el modo de redondeo de redondeo a medio redondeo). Anteriormente daba una cadena basada en redondear x a 17 dígitos decimales.

El problema relacionado


Más información: El formato floatanterior a Python 2.7 era similar al actual numpy.float64. Ambos tipos usan la misma precisión doble IEEE 754 de 64 bits con 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 pueda perder 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 otra parte:float.__repr__está formateado para que cada dígito sea importante; la secuencia no tiene espacios y la conversión es reversible. Simplemente: si tal vez tiene un número numpy.float64, conviértalo a flotante normal para formatearlo para humanos, no para procesadores numéricos, de lo contrario, no se necesita nada más con Python 2.7+.

hynekcer
fuente
¿Por qué votar abajo? La pregunta era sobre Python float(doble precisión) y normal round, no sobre numpy.double y su conversión a cadena. El redondeo simple de Python realmente no se puede hacer mejor que en Python 2.7. La mayoría de las respuestas se han escrito antes de 2.7, pero están obsoletas, aunque originalmente eran muy buenas. Esta es la razón de mi respuesta.
hynekcer
53 bits cuando se incluye el "bit oculto", que está implícitamente 1, excepto durante el "desbordamiento gradual".
Rick James
No es culpa de la ronda, es la falla de la pantalla.
Rick James
Sí, es bien sabido. Sin embargo, echo de menos un contexto si te opones a algo en las notas de lanzamiento de Python 2.7 o en mi texto o nada. Es más complicado de lo necesario el propósito de esta pregunta. Cabe agregar que también la conversión de cadena a flotante se ha corregido en Python 2.7 debido al error de redondeo en ciertos chips Intel de 32 bits y que "la función round () ahora también se redondea correctamente". ( Notas de la versión - Características 3.1 soportadas en 2.7 ). Puedes estar de acuerdo?
hynekcer
1
Vaya, que era a*bvs b*a. Gracias por los enlaces - Nostalgia.
Rick James
52

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 anteriores 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 sobre el formato de cadenas 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 los distintos indicadores .

Referencia: Convierta el número de coma flotante con cierta precisión y luego cópielo en una cadena

AJ
fuente
¿Cómo representas un número entero? Si uso el formato "{i3}". (Numvar) me sale un error.
skytux
Esto es lo que quiero decir: si numvar=12.456, entonces "{:.2f}".format(numvar)cede 12.46pero "{:2i}".format(numvar)da un error y estoy esperando 12.
skytux
50

Puede modificar el formato de salida:

>>> a = 13.95
>>> a
13.949999999999999
>>> print "%.2f" % a
13.95
Greg Hewgill
fuente
47

Nadie parece haberlo mencionado todavía, así que permítanme dar un ejemplo en el formato f-string / template-string de Python 3.6, que creo que está muy bien:

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

Funciona bien con ejemplos más largos también, con operadores y que no necesitan parens:

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

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

print(format(14.4499923, '.2f')) // output is 14.45
Asad Manzoor
fuente
44
Esto devuelve cadena
Jemshit Iskenderov
29

En Python 2.7:

a = 13.949999999999999
output = float("%0.2f"%a)
print output
Shashank Singh
fuente
1
Esto no ayuda en absoluto. outputtiene exactamente el mismo valor que a, por lo que bien podría haber escrito en print alugar de print outputen la última línea.
Mark Dickinson
@ MarkDickinson ¿Podrías intentarlo de nuevo? Porque se está ejecutando como se esperaba en mi compilador.
Shashank Singh el
1
Te estás perdiendo mi punto. Sí, tu código se imprime 13.95. Pero también lo hace print a, para este valor particular de a, en Python 2.7, por lo que no está realmente claro cuál era el punto del paso de formateo.
Mark Dickinson el
@ MarkDickinson He editado el código. Estoy de acuerdo en que 'imprimir a' imprime el mismo valor que "salida de impresión". Pero si compara "a == salida", el resultado será "Falso" porque el paso de formateo redondea el valor flotante "a" a dos puntos decimales.
Shashank Singh el
1
¿Realmente probaste a == outputel código que muestras? Da Truepor mí, y sospecho que también por ti.
Mark Dickinson el
22

El tutorial de Python tiene un apéndice llamado Aritmética de coma flotante: problemas y limitaciones . Léelo Explica qué 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

Es posible que sienta la tentación de utilizar la round() función para volver a cortarla en el único dígito que espera. Pero eso no hace ninguna diferencia:

>>> round(0.1, 1)
0.10000000000000001

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

Otra consecuencia es que, dado que 0.1 no es exactamente 1/10, sumar diez valores de 0.1puede no rendir exactamente 1.0, tampoco:

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

Una alternativa y solución a sus problemas sería usar el decimalmódulo.

nosklo
fuente
18

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

value = 2.34558
precision = 2
width = 4

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

que mostrará result: 2.35

toto_tico
fuente
11

Use la combinación del objeto Decimal y el método round ().

Python 3.7.3
>>> from decimal import Decimal
>>> d1 = Decimal (13.949999999999999) # define a Decimal
>>> d1 
Decimal('13.949999999999999289457264239899814128875732421875')
>>> d2 = round(d1, 2) # round to 2 decimals
>>> d2
Decimal('13.95')
Jonathan L
fuente
7

Para arreglar el punto flotante en lenguajes de tipo dinámico 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 puede usar Decimal de la siguiente manera:

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')
Siamand
fuente
1
getcontext().prec = 6funciona solo para el alcance de la función o para todos los lugares?
Julio Marins
1
Los contextos son entornos para operaciones aritméticas. Gobiernan la precisión, establecen reglas para el redondeo, determinan qué señales se tratan como excepciones y limitan el rango de los exponentes. Cada hilo tiene su propio contexto actual @JulioMarins
Siamand
7
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'
weaming
fuente
6
orig_float = 232569 / 16000.0

14.5355625

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

14,54

MikeL
fuente
5

¿Qué pasa con una función lambda como esta:

arred = lambda x,n : x*(10**n)//1/(10**n)

De esta manera, podrías hacer:

arred(3.141591657,2)

y obten

3.14
Gustavo Mirapalheta
fuente
4

Es simple como 1,2,3:

  1. usar decimal módulo para la aritmética rápida de coma flotante decimal correctamente redondeada:

    d = Decimal (10000000.0000009)

para lograr el redondeo:

   d.quantize(Decimal('0.01'))

será 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. Vota esta respuesta :)

PD: crítica de los demás: el formateo no es redondeado.

Sławomir Lenart
fuente
2

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
iblasi
fuente
no funciona para mí en python 3.4.3 y numpy 1.9.1? >>> importar numpy como np >>> res = 0.01 >>> valor = 0.184 >>> np.round (valor / res) * res 0.17999999999999999
szeitlin
1
Buscando documentación veo que el problema proviene de la numpy.roundprecisión / precisión. Por lo tanto, requiere definirlo como int antes de la multiplicación con resolución. Actualicé el código. ¡Gracias por eso!
iblasi
Lo único necesario es convertir el numpy.float64resultado de np.round ao floatsimplemente usarlo round(value, 2). No existe un número IEEE 754 válido entre 13.94999999999999999 (= 1395 / 100.) y 3.950000000000001 (= 1395 * .01). ¿Por qué crees que tu método es el mejor? El valor original 13.949999999999999289 (= value = round (value, 2)) es aún más exacto que su 13.95000000000000178 (impreso por np.float96). Ahora se agrega más información también para numpy a mi respuesta que probablemente desestimó su voto por error. No se trataba de numpy originalmente.
hynekcer
@hynekcer No creo que mi respuesta sea la mejor. Solo quería agregar un ejemplo de límite flotante a n decimales, pero el más cercano de una resolución definida. Verifiqué como dijiste, que en lugar de intti también puedes usar el floatejemplo @szeitlin. Gracias por tu comentario extra. (Lo siento pero no te
he votado mal
¿Añadir una dependencia completamente nueva para el procesamiento numérico (pandas) es la "mejor manera"?
Hejazzman
1

lambda x, n: int (x * 10 n + .5) / 10 n me ha funcionado durante muchos años en muchos idiomas.

Ray Tayek
fuente
-13

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

Primero, convierta el flotador en una cadena, luego elija la longitud que le gustaría que fuera.

float = str(float)[:5]

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

¡Espero que ayude!

tdh
fuente
2
No publiques respuestas idénticas a múltiples preguntas.
vaultah
18
WOW ... tdh ... Nunca haga ningún software de contabilidad ... ¿Qué pasa si el número es 113.94? esto daría como resultado 113.9 ... dejando 0.04 faltantes ... También esto ya tiene respuestas de hace más de 5 años ...
Enojado 84