Convertir un flotador de Python en una cadena sin perder precisión

Estoy manteniendo un script de Python que usa xlrd para recuperar valores de las hojas de cálculo de Excel y luego hacer varias cosas con ellos. Algunas de las celdas de la hoja de cálculo son números de alta precisión y deben permanecer como tales. Al recuperar los valores de una de estas celdas, xlrd me da un valor float como 0.38288746115497402.

Sin embargo, necesito obtener este valor en una cadena más adelante en el código. Si se hace str(value) o unicode(value) se devolverá algo como “0.382887461155”. Los requisitos dicen que esto no es aceptable; La precisión debe ser preservada.

He intentado un par de cosas hasta ahora sin éxito. El primero fue usar una cadena de formateo de formato:

 data = "%.40s" % (value) data2 = "%.40r" % (value) 

Pero ambos producen el mismo número redondeado, “0.382887461155”.

Al buscar personas con problemas similares en SO y en otras partes de Internet, una sugerencia común fue utilizar la clase Decimal . Pero no puedo cambiar la forma en que se me dan los datos (a menos que alguien conozca una forma secreta de hacer que xlrd devuelva xlrd ). Y cuando trato de hacer esto:

 data = Decimal(value) 

Obtengo un TypeError: Cannot convert float to Decimal. First convert the float to a string. TypeError: Cannot convert float to Decimal. First convert the float to a string. Pero obviamente no puedo convertirlo en una cadena, o perderé la precisión.

Así que sí, estoy abierto a cualquier sugerencia, incluso si es necesario. No tengo mucha experiencia con Python (más de un tipo de Java / C #), así que siéntete libre de corregirme si tengo algún tipo de malentendido fundamental aquí.

EDITAR: Pensé simplemente que agregaría que estoy usando Python 2.6.4. No creo que haya requisitos formales que me impidan cambiar de versión; Solo tiene que no estropear ninguno de los otros códigos.

Soy el autor de xlrd. Hay tanta confusión en otras respuestas y comentarios que refutar en comentarios, así que lo estoy haciendo en una respuesta.

@katriealex: “” “la precisión se pierde en las entrañas de xlrd” “” — totalmente infundada y falsa. xlrd reproduce exactamente el flotador de 64 bits almacenado en el archivo XLS.

@katriealex: “” “Puede ser posible modificar su instalación xlrd local para cambiar el modelo flotante” “” — No sé por qué querría hacer esto; no pierdes ninguna precisión al flotar un entero de 16 bits !!! En cualquier caso, ese código se usa solo al leer archivos Excel 2.X (que tenían un registro de celda de tipo INTEGER). El OP no da ninguna indicación de que esté leyendo archivos tan antiguos.

@jloubert: Debes estar equivocado. "%.40r" % a_float es solo una forma barroca de obtener la misma respuesta que repr(a_float) .

@ EVERYBODY: No es necesario convertir un flotante a un decimal para preservar la precisión. El punto repr() función repr() es que se garantiza lo siguiente:

 float(repr(a_float)) == a_float 

Python 2.X (X <= 6) repr proporciona una constante de 17 dígitos decimales de precisión, ya que está garantizado para reproducir el valor original. Los Pythons posteriores (2.7, 3.1) dan el número mínimo de dígitos decimales que reproducirán el valor original.

 Python 2.6.4 (r264:75708, Oct 26 2009, 08:23:19) [MSC v.1500 32 bit (Intel)] on win32 >>> f = 0.38288746115497402 >>> repr(f) '0.38288746115497402' >>> float(repr(f)) == f True Python 2.7 (r27:82525, Jul 4 2010, 09:01:59) [MSC v.1500 32 bit (Intel)] on win32 >>> f = 0.38288746115497402 >>> repr(f) '0.382887461154974' >>> float(repr(f)) == f True 

Entonces, la conclusión es que si desea una cadena que conserva toda la precisión de un objeto flotante, use preserved = repr(the_float_object) … recupere el valor más tarde mediante float(preserved) . Es así de simple. No es necesario el módulo decimal .

Puedes usar repr() para convertir a una cadena sin perder precisión, luego convertir a un decimal:

 >>> from decimal import Decimal >>> f = 0.38288746115497402 >>> d = Decimal(repr(f)) >>> print d 0.38288746115497402 

EDITAR: Estoy equivocado. Dejaré esta respuesta aquí para que el rest del hilo tenga sentido, pero no es cierto. Por favor vea la respuesta de John Machin arriba. Gracias chicos =).

Si las respuestas anteriores funcionan bien, te ahorrará un montón de piratería desagradable. Sin embargo, al menos en mi sistema, no lo harán. Puede comprobar esto con por ejemplo

 import sys print( "%.30f" % sys.float_info.epsilon ) 

Ese número es el flotador más pequeño que su sistema puede distinguir de cero. Cualquier cosa más pequeña que eso puede ser agregada o sustraída al azar de cualquier flotador cuando realiza una operación. Esto significa que, al menos en mi configuración de Python, la precisión se pierde dentro de las entrañas de xlrd , y parece que no hay nada que puedas hacer sin modificarlo. Lo cual es extraño; ¡Hubiera esperado que este caso hubiera ocurrido antes, pero aparentemente no!

Puede ser posible modificar su instalación xlrd local para cambiar el modelo float . Abra site-packages\xlrd\sheet.py y vaya a la línea 1099:

 ... elif rc == XL_INTEGER: rowx, colx, cell_attr, d = local_unpack(' 

Observe el modelo float : podría intentar cambiarlo a un decimal.Decimal y ver qué sucede.

EDITAR: Se borró mi respuesta anterior b / c no funcionó correctamente.

Estoy en Python 2.6.5 y esto funciona para mí:

 a = 0.38288746115497402 print repr(a) type(repr(a)) #Says it's a string 

Nota: Esto simplemente se convierte en una cadena. Tendrá que convertir a Decimal usted mismo más tarde si es necesario.

Como ya se ha dicho, un flotador no es preciso en absoluto, por lo que preservar la precisión puede ser algo engañoso.

Aquí hay una manera de obtener hasta el último bit de información de un objeto flotante:

 >>> from decimal import Decimal >>> str(Decimal.from_float(0.1)) '0.1000000000000000055511151231257827021181583404541015625' 

Otra forma sería así.

 >>> 0.1.hex() '0x1.999999999999ap-4' 

Ambas cadenas representan el contenido exacto del flotador. Casi cualquier otra cosa interpreta el flotador como python cree que probablemente fue pensado (lo que la mayoría de las veces es correcto).