Pregunta

estoy manteniendo una secuencia de comandos de Python que xlrd usos para recuperar los valores de las hojas de cálculo de Excel, y luego hacer varias cosas con ellos. Algunas de las células en 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 células, xlrd me da una float como 0.38288746115497402.

Sin embargo, tengo que obtener este valor en una cadena más adelante en el código. La realización de cualquiera str(value) o unicode(value) volverá algo así como ",382887461155". Los requisitos dicen que esto no es aceptable; la precisión debe ser preservado.

he intentado un par de cosas hasta ahora sin éxito. El primero fue el uso de un formato de cadenas cosita:

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

Sin embargo, ambos productos el mismo número redondeado, ",382887461155".

Al buscar en torno a las personas con problemas similares en SO y en otros lugares en el Internet, una sugerencia común era utilizar la clase Decimal. Pero no puedo cambiar la forma de administración de los datos a mí (a menos que alguien sabe de una manera secreta para hacer Decimales retorno xlrd). Y cuando trato de hacer esto:

data = Decimal(value)

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

Así que sí, estoy abierto a cualquier sugerencia - incluso los / las hacky muy brutos, si es necesario. No estoy terriblemente experimentado con Python (más de un tipo de Java # / C yo) así que no dude en corregirme si tengo algún tipo de malentendido fundamental aquí.

EDIT: Sólo pensaba que iba a añadir que estoy usando Python 2.6.4. Creo que no hay ningún requisito formal me impide cambiar la versión; sólo tiene que no estropear cualquiera de los otros códigos.

¿Fue útil?

Solución

Soy el autor de XLRD. Hay mucha confusión en otras respuestas y comentarios de refutar en los comentarios, así que estoy haciendo en una respuesta.

@katriealex: "" "precisión que se pierde en las entrañas de XLRD" "" --- totalmente infundada y falsa. XLRD reproduce exactamente el flotador de 64 bits que se almacena en el archivo XLS.

@katriealex: "" "Puede ser posible modificar la instalación XLRD local para cambiar el reparto de flotación" "" --- No sé por qué querría hacer esto; que no pierda ninguna precisión al flotar un entero de 16 bits !!! En cualquier caso, ese código se utiliza sólo cuando la lectura de archivos Excel 2.X (que tenían un registro de células de tipo INTEGER). El PO no da ninguna indicación de que él está leyendo este tipo de archivos antiguos.

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

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

float(repr(a_float)) == a_float

Python 2.X (X <= 6) repr da una constante de 17 dígitos decimales de precisión, como que está garantizado para reproducir el valor original. Más tarde pitones (2.7, 3.1) da el número mínimo de dígitos decimales que reproducirá 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

Así que la conclusión es que si quieres una cadena que conserva toda la precisión de un objeto flotante, preserved = repr(the_float_object) uso ... recuperar el valor más tarde por float(preserved). Es así de simple. No hay necesidad de que el módulo de decimal.

Otros consejos

Se puede utilizar repr() a convertir en una cadena sin perder precisión, a continuación, convertir a un decimal:

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

EDIT: estoy equivocado. Voy a dejar esta respuesta aquí así que el resto de la rosca tiene sentido, pero no es cierto. Por favor, véase la respuesta de John Machin anteriormente. Gracias chicos =).

Si las respuestas anteriores funcionan que es grande - que le ahorrará una gran cantidad de piratería desagradable. Sin embargo, al menos en mi sistema, no lo harán. Esto se puede comprobar 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 se puede agregar al azar o se resta de cualquier flotador cuando se realiza una operación. Esto significa que, al menos en mi programa de instalación de Python, la precisión se pierde en las entrañas de xlrd, y no parece haber nada se puede hacer sin modificarlo. Lo cual es extraño; Me hubiera esperado este caso de haber ocurrido antes, pero al parecer no!

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

...
elif rc == XL_INTEGER:
                    rowx, colx, cell_attr, d = local_unpack('<HH3sH', data)
                    self_put_number_cell(rowx, colx, float(d), self.fixed_BIFF2_xfindex(cell_attr, rowx, colx))
...

Aviso elenco float -. Usted podría intentar cambiar eso a un decimal.Decimal y ver lo que sucede

EDIT:. debitado de mi respuesta anterior b / c no funcionaba 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 sólo se convierte en una cadena. Tendrá que convertirse al mismo Decimal tarde si es necesario.

Como ya se ha dicho, un flotador no es precisa en absoluto -. Preservando así la precisión puede ser algo engañoso

Esta es una manera de conseguir hasta el último bit de la información de un objeto flotante:

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

Otra forma sería como tal.

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

Las dos cadenas representan el contenido exacto de la boya. Cualquier otra cosa allmost interpreta el flotador como pitón piensa que probablemente se pretendía (que la mayoría de las veces es correcto).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top