Formatear flotadores en Python sin ceros superfluos


Cómo dar formato a un flotador para que no se contener los restantes ceros? En otras palabras, quiero que la cadena resultante sea lo más corta posible..?

Como:

3 -> "3"
3. -> "3"
3.0 -> "3"
3.1 -> "3.1"
3.14 -> "3.14"
3.140 -> "3.14"
Author: Jon Clements, 2010-03-14

14 answers

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

 138
Author: Alex Martelli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-14 01:11:24

Puedes usar %g para lograr esto:

'%g'%(3.140)

O, para Python 2.6 o superior:

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

De los documentos para format: g causas (entre otras cosas)

Ceros finales insignificantes [ser] de la significancia, y la punto decimal también se elimina si hay no quedan dígitos siguiéndolo.

 114
Author: unutbu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-14 00:39:34

Después de revisar las respuestas a varias preguntas similares, esta parece ser la mejor solución para mí: {[18]]}

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 un comportamiento extraño y tiene 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 format(inputValue, '.15f'). en lugar de '%.15f' % inputValue, pero eso es un poco más lento (~30%).

Podría haber usado Decimal(inputValue).normalize(), pero esto también tiene algunos problemas. Por un lado, es un Mucho más lento (~11x). También descubrí que aunque tiene una precisión bastante grande, todavía sufre de pérdida de precisión al usar normalize().

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

Lo más importante, todavía estaría convirtiendo a Decimal de un float que puede hacer que termines con algo que no sea el número que pones allí. Creo que Decimal funciona mejor cuando la aritmética permanece en Decimal y el Decimal se 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 de Decimal.normalize() se puede ajustar a lo que es necesitaba usar la configuración de contexto, pero teniendo en cuenta la velocidad ya lenta y no necesitar 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 pensé que valiera la pena seguir.

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

def floatToString(inputValue):
    result = ('%.15f' % inputValue).rstrip('0').rstrip('.')
    return '0' if result == '-0' else result
 9
Author: PolyMesh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-09 21:17:09

¿Qué hay de probar el enfoque más fácil y probablemente más eficaz? El método normalize() elimina todos los ceros finales situados 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 @BobStein-VisiBone señaló, es que números como 10, 100, 1000... se mostrará en representación exponencial. Esto se puede arreglar fácilmente usando la siguiente función en su lugar:

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()
 9
Author: Ander,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-13 13:55:15

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

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
 3
Author: Kaushal Modi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-22 14:42:10

Simplemente puedes usar format() para lograr esto:

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

 2
Author: clel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-17 15:21:37
>>> str(a if a % 1 else int(a))
 1
Author: Shameem,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-28 11:10:51

Mientras que el formato es probable que la forma más pitónica, aquí hay una solución alternativa utilizando la more_itertools.rstrip herramienta.

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 elimina de los caracteres finales que satisfacen un predicado. La definición de la función fmt no es necesaria, pero se utiliza aquí para probar aserciones, que todas pasan. Nota: funciona con entradas de cadena y acepta predicados opcionales.

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

 1
Author: pylang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-13 17:59:59

Si puedes vivir con 3. y 3.0 aparece como "3.0", un enfoque muy simple que elimina ceros de las representaciones flotantes:

print("%s"%3.140)

(gracias @ellimilial por señalar las excepciones)

 1
Author: drevicko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-07 03:41:17

Use %g con suficiente ancho, 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'
 0
Author: alexanderlukanin13,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-17 14:48:46

OP desea eliminar 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ño.

Aquí hay una forma de formatear números como cadenas cortas que usa %g notación exponencial solo cuando es decimal.normalizar crea cadenas que son demasiado largas. Esta podría no ser la solución más rápida (ya que utiliza Decimal.normalizar)

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
Author: kinok,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-06 20:39:35

Para float podrías usar esto:

def format_float(num):
    return ('%i' if num == int(num) else '%s') % num

Pruébalo:

>>> format_float(1.00000)
'1'
>>> format_float(1.1234567890000000000)
'1.123456789'

Para Decimal ver solución aquí: https://stackoverflow.com/a/42668598/5917543

 0
Author: Artem Skoretskiy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:30

Puedes usar max() así:

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

 -1
Author: elig,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-08 23:28:44

Puedes lograr eso de la manera más pitónica así:

Python3:

"{:0.0f}".format(num)
 -3
Author: Lan Vukušič,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-11 18:02:39