Question

Je suis maintenant un script Python que les utilisations xlrd pour récupérer les valeurs à partir des feuilles de calcul Excel, puis faire des choses différentes avec eux. Certaines des cellules dans la feuille de calcul sont des nombres de haute précision, et ils doivent le rester. Lors de la récupération des valeurs d'une de ces cellules, xlrd me donne un float tel que ,38288746115497402.

Cependant, je dois obtenir cette valeur en une chaîne plus tard dans le code. Faire soit str(value) ou unicode(value) retournera quelque chose comme « 0,382887461155 ». Les exigences disent que ce n'est pas acceptable; la précision doit être préservée.

J'ai essayé deux choses jusqu'ici sans succès. La première est une chaîne en utilisant le formatage thingy:

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

Mais les deux produisent le même nombre arrondi, « 0,382887461155 ».

Lors de la recherche autour des personnes ayant des problèmes similaires sur SO et ailleurs sur Internet, une suggestion commune était d'utiliser la classe Decimal. Mais je ne peux pas changer la façon dont les données me est donnée (à moins que quelqu'un sait d'une façon secrète pour faire DECIMALS retour de xlrd). Et quand je tente de faire ceci:

data = Decimal(value)

Je reçois un TypeError: Cannot convert float to Decimal. First convert the float to a string. Mais je ne peux évidemment pas le convertir en une chaîne, ou bien je vais perdre la précision.

Alors oui, je suis ouvert à toute suggestion - même vraiment les bruts / aki si nécessaire. Je ne suis pas terriblement expérimenté avec Python (plus d'un type Java / C # moi-même) ne hésitez pas à me corriger si j'ai une sorte de malentendu fondamental.

EDIT: Juste pensé que je voudrais ajouter que je suis en utilisant Python 2.6.4. Je ne pense pas qu'il y ait des exigences formelles me arrêtant des versions changeantes; il a juste de ne pas gâcher une de l'autre code.

Était-ce utile?

La solution

Je suis l'auteur de xlrd. Il y a tellement de confusion dans d'autres réponses et commentaires à réfuter dans les commentaires, donc je le fais dans une réponse.

@katriealex: « » « précision se perdre dans les entrailles de xlrd » « » --- dénuées de tout fondement et faux. xlrd reproduit exactement le flotteur 64 bits qui est stocké dans le fichier XLS.

@katriealex: « » « Il est possible de modifier votre installation de xlrd local pour changer la distribution float » « » --- Je ne sais pas pourquoi vous voulez le faire; vous ne perdez pas de précision par un entier flottant 16 bits !!! En tout cas, ce code est utilisé uniquement lors de la lecture des fichiers Excel 2.x (qui avait un dossier de cellules de type ENTIER). L'OP ne donne aucune indication qu'il est en train de lire ces fichiers anciens.

@jloubert: Vous devez vous tromper. "%.40r" % a_float est juste une façon baroque d'obtenir la même réponse que repr(a_float).

@EVERYBODY: Vous n'avez pas besoin de convertir un flotteur à une décimale pour préserver la précision. Le point de l'ensemble de la fonction repr() est que le suivant est garanti:

float(repr(a_float)) == a_float

Python 2.X (X <= 6) repr donne une constante de 17 chiffres décimaux de précision, comme cela est garanti pour reproduire la valeur d'origine. Plus tard Pythons (2.7, 3.1) donne le nombre minimal de chiffres décimaux qui reproduit la valeur d'origine.

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

Ainsi, la ligne de fond est que si vous voulez une chaîne qui conserve toute la précision d'un objet flottant, utilisez preserved = repr(the_float_object) ... récupérer la valeur plus tard par float(preserved). Il est aussi simple que cela. Pas besoin du module decimal.

Autres conseils

Vous pouvez utiliser repr() pour convertir en une chaîne sans perte de précision, puis convertir en décimal:

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

EDIT: Je me trompe. Je quitterai cette réponse ici pour que le reste du fil est logique, mais il est pas vrai. S'il vous plaît voir la réponse de John Machin ci-dessus. Merci les gars =).

Si les réponses ci-dessus fonctionnent c'est génial - il vous fera économiser beaucoup de hacking méchant. Cependant, au moins sur mon système, ils ne seront pas. Vous pouvez le vérifier avec par exemple.

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

Ce numéro est le plus petit flotteur que votre système peut distinguer de zéro. Tout ce plus petit que celui peut être aléatoire ajouté ou soustrait de tout flotteur lorsque vous effectuez une opération. Cela signifie que, au moins sur ma configuration Python, la précision est perdu dans les entrailles de xlrd, et il semble y avoir rien vous pouvez le faire sans le modifier. Ce qui est étrange; Je l'aurais prévu ce cas ait eu lieu avant, mais apparemment pas!

Il est possible de modifier votre installation de xlrd local pour changer la distribution de float. Ouvrez site-packages\xlrd\sheet.py et descendez à la ligne 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))
...

Notez la distribution de float -. Vous pouvez essayer de changer cela à un decimal.Decimal et voir ce qui se passe

EDIT:. Effacé ma réponse précédente b / c il n'a pas fonctionné correctement

Je suis sur Python 2.6.5 et cela fonctionne pour moi:

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

Note: Cette seulement convertit à une chaîne. Vous devrez convertir pour vous Decimal plus tard si nécessaire.

Comme il a été dit, un flotteur est pas précis du tout -. Préservant ainsi la précision peut être quelque peu trompeur

Voici une façon d'obtenir chaque bit d'information sur un objet flottant:

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

Une autre façon serait comme si.

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

Les deux chaînes représentent le contenu exact du flotteur. Allmost quoi que ce soit d'autre interprète le flotteur comme python pense qu'il a probablement été conçu (dont la plupart du temps est correct).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top