Formateo de flotantes sin ceros finales


181

¿Cómo puedo formatear un flotante para que no contenga ceros finales? En otras palabras, quiero que la cadena resultante sea lo más corta posible.

Por ejemplo:

3 -> "3"
3. -> "3"
3.0 -> "3"
3.1 -> "3.1"
3.14 -> "3.14"
3.140 -> "3.14"

Ese ejemplo no tiene ningún sentido en absoluto. 3.14 == 3.140- Son el mismo número de coma flotante. Por lo demás, 3.140000 es el mismo número de coma flotante. El cero no existe en primer lugar.
S.Lott

33
@ S.Lott: creo que el problema es IMPRIMIR el número flotante sin los ceros finales, no la equivalencia real de dos números.
pokstad

1
@pokstad: en cuyo caso, no hay cero "superfluo". %0.2fy %0.3fson los dos formatos requeridos para producir los últimos números a la izquierda. Use %0.2fpara producir los dos últimos números a la derecha.
S.Lott

66
3.0 -> "3"sigue siendo un caso de uso válido. print( '{:,g}'.format( X )funcionó para mí para producir 3dónde X = 6 / 2y cuándo X = 5 / 2obtuve una salida de 2.5lo esperado.
ShoeMaker

1
vieja pregunta, pero ... print("%s"%3.140)te da lo que quieres. (
Agregué

Respuestas:


178

Yo, lo haría ('%f' % x).rstrip('0').rstrip('.'), garantiza el formato de punto fijo en lugar de la notación científica, etc. Sí, no es tan elegante y elegante como %g, pero funciona (y no sé cómo forzar %ga nunca usar la notación científica; -).


66
El único problema con eso es que '%.2f' % -0.0001te dejará -0.00y finalmente -0.
Kos

3
@ alexanderlukanin13 porque la precisión predeterminada es 6, consulte docs.python.org/2/library/string.html : 'f' Fixed point. Displays the number as a fixed-point number. The default precision is 6.tendría que usar '% 0.7f' en la solución anterior.
derenio

3
@derenio Buen punto :-) Solo puedo agregar que aumentar la precisión anterior '%0.15f'es una mala idea, porque comienzan a suceder cosas extrañas .
alexanderlukanin13

1
En caso de que esté en medio de alguna otra cadena: print('In the middle {} and something else'.format('{:f}'.format(a).rstrip('0')))
con tolerancia a fallas

1
@ Alex Martelli, deshazte del doble rstripcomando. Simplemente use esto en su lugar para quitar el punto ( .) y todos los ceros finales después en una sola operación:('%f' % x).rstrip('.0')
Gabriel Staples

143

Podrías usar %gpara lograr esto:

'%g'%(3.140)

o, para Python 2.6 o superior:

'{0:g}'.format(3.140)

De los documentos paraformat : gcausas (entre otras cosas)

los ceros finales insignificantes [se eliminarán] del significado, y el punto decimal también se eliminará si no quedan dígitos que lo sigan.


28
¡Oh casi! A veces formatea el flotador en notación científica ("2.342E + 09"). ¿Es posible desactivarlo, es decir, siempre mostrar todos los dígitos significativos?
TarGz

55
¿Por qué usar '{0:...}'.format(value)cuando podrías usar format(value, '...')? Eso evita tener que analizar el especificador de formato de una cadena de plantilla que, de lo contrario, está vacía.
Martijn Pieters

2
@MartijnPieters: el costo minúsculo de analizar el especificador de formato es abrumado por otro AFAICT; en la práctica, mis puntos de referencia locales en 3.6 (con el alcance de la función del microbenchmark para modelar con precisión el código real) han tardado format(v, '2.5f')~ 10% más que '{:2.5f}'.format(v). Incluso si no fuera así, tiendo a usar el strformulario de método porque cuando necesito modificarlo, agregarle valores adicionales, etc., hay menos para cambiar. Por supuesto, a partir de 3.6 tenemos cadenas f para la mayoría de los propósitos. :-)
ShadowRanger

55
En Python 3.6, esto se puede acortar a f"{var:g}"donde varhay una variable flotante.
TheGreatCabbage

12

¿Qué hay de intentar el enfoque más fácil y probablemente más efectivo? El método normalize () elimina todos los ceros finales más a la derecha.

from decimal import Decimal

print (Decimal('0.001000').normalize())
# Result: 0.001

Funciona en Python 2 y Python 3 .

-- Actualizado --

El único problema, como señaló @ BobStein-VisiBone, es que los números como 10, 100, 1000 ... se mostrarán en representación exponencial. Esto se puede solucionar fácilmente utilizando la siguiente función:

from decimal import Decimal


def format_float(f):
    d = Decimal(str(f));
    return d.quantize(Decimal(1)) if d == d.to_integral() else d.normalize()

2
Excepto Decimal('10.0').normalize()se convierte en'1E+1'
Bob Stein

11

Después de buscar respuestas a varias preguntas similares, esta parece ser la mejor solución para mí:

def floatToString(inputValue):
    return ('%.15f' % inputValue).rstrip('0').rstrip('.')

Mi razonamiento:

%g no se deshace de la notación científica.

>>> '%g' % 0.000035
'3.5e-05'

15 decimales parecen evitar comportamientos extraños y tienen mucha precisión para mis necesidades.

>>> ('%.15f' % 1.35).rstrip('0').rstrip('.')
'1.35'
>>> ('%.16f' % 1.35).rstrip('0').rstrip('.')
'1.3500000000000001'

Podría haber usado en format(inputValue, '.15f').lugar de '%.15f' % inputValue, pero eso es un poco más lento (~ 30%).

Podría haberlo usado Decimal(inputValue).normalize(), pero esto también tiene algunos problemas. Por un lado, es MUCHO más lento (~ 11x). También descubrí que aunque tiene una precisión bastante grande, aún sufre pérdida de precisión cuando se usa normalize().

>>> Decimal('0.21000000000000000000000000006').normalize()
Decimal('0.2100000000000000000000000001')
>>> Decimal('0.21000000000000000000000000006')
Decimal('0.21000000000000000000000000006')

Lo que es más importante, todavía me convertiría Decimalde una floatque puede hacer que termines con algo más que el número que pones allí. Creo que Decimalfunciona mejor cuando la aritmética permanece Decimaly Decimalse inicializa con una cadena.

>>> Decimal(1.35)
Decimal('1.350000000000000088817841970012523233890533447265625')
>>> Decimal('1.35')
Decimal('1.35')

Estoy seguro de que el problema de precisión Decimal.normalize()se puede ajustar a lo que se necesita usando la configuración de contexto, pero teniendo en cuenta la velocidad ya lenta y no necesita una precisión ridícula y el hecho de que todavía estaría convirtiendo de un flotador y perdiendo precisión de todos modos, no lo hice No creo que valiera la pena perseguirlo.

No estoy preocupado con el posible resultado "-0" ya que -0.0 es un número de coma flotante válido y probablemente sea una ocurrencia rara de todos modos, pero como mencionó que desea mantener el resultado de la cadena lo más corto posible, usted siempre podría usar un condicional adicional a muy bajo costo de velocidad adicional.

def floatToString(inputValue):
    result = ('%.15f' % inputValue).rstrip('0').rstrip('.')
    return '0' if result == '-0' else result

1
Desafortunadamente solo funciona con números con menos de aproximadamente) cinco o más dígitos a la izquierda del decimal. floatToString(12345.6)vuelve '12345.600000000000364'por ejemplo. Disminuir el 15 en %.15fun número menor lo resuelve en este ejemplo, pero ese valor debe reducirse cada vez más a medida que el número aumenta. Podría calcularse dinámicamente en función del log-base-10 del número, pero eso rápidamente se vuelve muy complicado.
JohnSpeeks

1
Una forma de resolver ese problema podría ser limitar la longitud del número entero (en lugar de solo los dígitos después del decimal):result = ('%15f' % val).rstrip('0').rstrip('.').lstrip(' ')
Timothy Smith

@ JohnSpeeks No estoy seguro de que esto sea evitable. Es un efecto secundario de los números flotantes que no pueden representar la precisión si se requieren más dígitos en el lado izquierdo. Por lo que puedo decir, el número que sale como una cadena es el mismo número que entra como flotante, o al menos la representación más cercana. >>>12345.600000000000364 == 12345.6 True
PolyMesh

Escribí otra solución .
niitsuma

8

Aquí hay una solución que funcionó para mí. Es una combinación de la solución de PolyMesh y el uso de la nueva .format() sintaxis .

for num in 3, 3., 3.0, 3.1, 3.14, 3.140:
    print('{0:.2f}'.format(num).rstrip('0').rstrip('.'))

Salida :

3
3
3
3.1
3.14
3.14

Lo único malo de este es que debe establecer un número razonable de dígitos decimales. Cuanto más alto lo establezca, más números precisos puede representar, pero si lo hace mucho, puede degradar el rendimiento.
beruic

1
Además del comentario de beruic, esto no funciona para carrozas de mayor precisión (por ejemplo 3.141), ya que .2festá codificado.
TrebledJ

3

Simplemente puede usar format () para lograr esto:

format(3.140, '.10g') donde 10 es la precisión que quieres.


2
>>> str(a if a % 1 else int(a))

No quiere decir int(a) if a % 1 else a?
beruic

Estimado Beruic, tu respuesta resulta negativa. a if a % 1 else int(a)es correcto. La pregunta necesita salida en cadena, así que acabo de agregarstr
Shameem

Ah, lo entiendo ahora. a % 1es verdad porque no es cero. Lo percibí implícita y erróneamente como a % 1 == 0.
beruic

2

Si bien es probable que el formato sea la forma más pitónica, aquí hay una solución alternativa que usa la more_itertools.rstripherramienta.

import more_itertools as mit


def fmt(num, pred=None):
    iterable = str(num)
    predicate = pred if pred is not None else lambda x: x in {".", "0"}
    return "".join(mit.rstrip(iterable, predicate))

assert fmt(3) == "3"
assert fmt(3.) == "3"
assert fmt(3.0) == "3"
assert fmt(3.1) == "3.1"
assert fmt(3.14) == "3.14"
assert fmt(3.140) == "3.14"
assert fmt(3.14000) == "3.14"
assert fmt("3,0", pred=lambda x: x in set(",0")) == "3"

El número se convierte en una cadena, que se despoja de los caracteres finales que satisfacen un predicado. La definición de la función fmtno es necesaria, pero se usa aquí para probar aserciones, que todas pasan. Nota: funciona en entradas de cadena y acepta predicados opcionales.

Ver también los detalles en esta biblioteca de terceros, more_itertools.


1

Si puede vivir con 3. y 3.0 que aparecen como "3.0", un enfoque muy simple que elimina los ceros a la derecha de las representaciones flotantes:

print("%s"%3.140)

(gracias @ellimilial por señalar las excepciones)


1
Pero lo print("%s"%3.0)hace.
ellimilial

1

Usar el paquete QuantiPhy es una opción. Normalmente, QuantiPhy se usa cuando se trabaja con números con unidades y factores de escala SI, pero tiene una variedad de buenas opciones de formato de números.

    >>> from quantiphy import Quantity

    >>> cases = '3 3. 3.0 3.1 3.14 3.140 3.14000'.split()
    >>> for case in cases:
    ...    q = Quantity(case)
    ...    print(f'{case:>7} -> {q:p}')
          3 -> 3
         3. -> 3
        3.0 -> 3
        3.1 -> 3.1
       3.14 -> 3.14
      3.140 -> 3.14
    3.14000 -> 3.14

Y no usará la notación electrónica en esta situación:

    >>> cases = '3.14e-9 3.14 3.14e9'.split()
    >>> for case in cases:
    ...    q = Quantity(case)
    ...    print(f'{case:>7} -> {q:,p}')
    3.14e-9 -> 0
       3.14 -> 3.14
     3.14e9 -> 3,140,000,000

Una alternativa que podría preferir es usar factores de escala SI, quizás con unidades.

    >>> cases = '3e-9 3.14e-9 3 3.14 3e9 3.14e9'.split()
    >>> for case in cases:
    ...    q = Quantity(case, 'm')
    ...    print(f'{case:>7} -> {q}')
       3e-9 -> 3 nm
    3.14e-9 -> 3.14 nm
          3 -> 3 m
       3.14 -> 3.14 m
        3e9 -> 3 Gm
     3.14e9 -> 3.14 Gm

0

OP desea eliminar los ceros superfluos y hacer que la cadena resultante sea lo más corta posible.

Encuentro que el formato exponencial% g acorta la cadena resultante para valores muy grandes y muy pequeños. El problema viene para valores que no necesitan notación exponencial, como 128.0, que no es ni muy grande ni muy pequeña.

Aquí hay una forma de formatear números como cadenas cortas que usa notación exponencial% g solo cuando Decimal.normalize crea cadenas que son demasiado largas. Es posible que esta no sea la solución más rápida (ya que utiliza Decimal.normalize)

def floatToString (inputValue, precision = 3):
    rc = str(Decimal(inputValue).normalize())
    if 'E' in rc or len(rc) > 5:
        rc = '{0:.{1}g}'.format(inputValue, precision)        
    return rc

inputs = [128.0, 32768.0, 65536, 65536 * 2, 31.5, 1.000, 10.0]

outputs = [floatToString(i) for i in inputs]

print(outputs)

# ['128', '32768', '65536', '1.31e+05', '31.5', '1', '10']



0

Aquí está la respuesta:

import numpy

num1 = 3.1400
num2 = 3.000
numpy.format_float_positional(num1, 3, trim='-')
numpy.format_float_positional(num2, 3, trim='-')

salida "3.14" y "3"

trim='-' elimina tanto los ceros finales como el decimal.


-1

Utilice% g con ancho suficientemente grande, por ejemplo '% .99g'. Se imprimirá en notación de punto fijo para cualquier número razonablemente grande.

EDITAR: no funciona

>>> '%.99g' % 0.0000001
'9.99999999999999954748111825886258685613938723690807819366455078125e-08'

1
.99es precisión, no ancho; un poco útil, pero no puede establecer la precisión real de esta manera (aparte de truncarla usted mismo).
Kos

-1

Puedes usar max()así:

print(max(int(x), x))


1
tienes que considerar el caso donde xes negativo. if x < 0: print(min(x), x) else : print(max(x), x)
ThunderPhoenix

Un método útil cuando quiero hacer json stringify. Float 1.0 cambia a int 1, por lo que funciona igual que en JavaScript.
pingze

-3

Puede lograr eso de la manera más pitónica de esa manera:

python3:

"{:0.0f}".format(num)

Tienes razón. La forma más fácil es usar "{: g}". Format (num)
Pyglouthon

-4

Manejando% f y deberías poner

% .2f

, donde: .2f == .00 flotantes.

Ejemplo:

imprimir "Precio:% ​​.2f"% precios [producto]

salida:

Precio: 1.50


1
Esto se afirma claramente, no lo que la pregunta quiere. Necesito un voto negativo.
episodeyang
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.