Domanda

Sto mantenendo uno script Python che utilizzi xlrd per recuperare i valori da fogli di calcolo Excel, e poi fare varie cose con loro. Alcune delle celle del foglio sono numeri di alta precisione, e devono rimanere tale. Quando si recuperano i valori di una di queste celle, xlrd mi dà un float come 0,38288746115497402.

Tuttavia, ho bisogno di ottenere questo valore in una stringa più avanti nel codice. Effettuando una str(value) o unicode(value) restituirà qualcosa come ",382887461155". I requisiti dicono che questo non è accettabile; la precisione ha bisogno di essere conservato.

Ho provato un paio di cose finora senza successo. Il primo è stato utilizzando una stringa di formattazione thingy:

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

Ma sia produrre lo stesso numero arrotondato, "0,382887461155".

Al momento la ricerca intorno per le persone con problemi simili su SO e altrove su Internet, un suggerimento comune è stato quello di utilizzare la classe Decimal. Ma non posso cambiare il modo in cui i dati sono dato a me (a meno che qualcuno conosce un modo segreto per fare ritorno decimali xlrd). E quando cerco di fare questo:

data = Decimal(value)

ho un TypeError: Cannot convert float to Decimal. First convert the float to a string. Ma ovviamente non posso convertirlo in una stringa, altrimenti perderò la precisione.

Quindi sì, io sono aperto a qualsiasi suggerimento - / quelli hacky anche davvero indecente se necessario. Io non sono terribilmente sperimentato con Python (più di una Java / C # ragazzo me) quindi sentitevi liberi di correggermi se ho qualche tipo di incomprensione fondamentale qui.

EDIT: Solo pensato Vorrei aggiungere che sto usando Python 2.6.4. Non credo che ci sono requisiti formali mi impedisce di cambiare le versioni; è solo per non rovinare uno qualsiasi degli altri codici.

È stato utile?

Soluzione

Sono l'autore di XLRD. C'è tanta confusione in altre risposte e commenti di confutare nei commenti così lo sto facendo in una risposta.

@katriealex: "" "di precisione sta perdendo nelle budella di XLRD" "" --- del tutto infondata e falsa. XLRD riproduce esattamente il galleggiante a 64 bit che è memorizzato nel file XLS.

@katriealex: "" "Potrebbe essere possibile modificare l'installazione XLRD locale per cambiare il cast galleggiante" "" --- Non so perché si vuole fare questo; non si perde una certa precisione galleggiando un intero a 16 bit !!! In ogni caso, che il codice è usato solo quando la lettura dei file Excel 2.X (che ha avuto un record di cellule INTERO-tipo). L'OP non fornisce alcuna indicazione che egli sta leggendo tali file antichi.

@jloubert: Devi essere scambiato. "%.40r" % a_float è solo un modo barocco di ottenere la stessa risposta repr(a_float).

@EVERYBODY: Non è necessario convertire un galleggiante in un decimale per preservare la precisione. Il punto della funzione repr() è che il seguente è garantita:

float(repr(a_float)) == a_float

Python 2.X (X <= 6) repr fornisce una costante 17 cifre decimali di precisione, dato che questo è garantita per riprodurre il valore originale. Più tardi Pitoni (2.7, 3.1) danno il numero minimo di cifre decimali che riprodurrà il valore originale.

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

Così la linea di fondo è che se si desidera una stringa che conserva tutta la precisione di un oggetto galleggiante, l'uso preserved = repr(the_float_object) ... Recuperare il valore più tardi da float(preserved). E 'così semplice. Non c'è bisogno per il modulo decimal.

Altri suggerimenti

È possibile utilizzare repr() per convertire in una stringa, senza perdere in precisione, poi convertire in un decimale:

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

EDIT: mi sbaglio. Lascerò questa risposta qui in modo che il resto del filo ha un senso, ma non è vero. Si prega di vedere la risposta di John Machin sopra. Grazie ragazzi =).

Se le risposte di cui sopra lavoro che è grande - vi farà risparmiare un sacco di brutta di hacking. Tuttavia, almeno sul mio sistema, non lo faranno. È possibile controllare questo con es.

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

Questo numero è il galleggiante più piccolo che il sistema è in grado di distinguere da zero. Qualcosa di più piccolo di quello può essere aggiunto in modo casuale o sottratto da qualsiasi galleggiante quando si esegue un'operazione. Questo significa che, almeno sul mio setup Python, la precisione si perde dentro le viscere di xlrd, e sembra che ci sia nulla si può fare senza modificarlo. Il che è strano; Mi sarei aspettato questo caso si sono verificati prima, ma a quanto pare non!

E 'possibile modificare l'installazione xlrd locale per cambiare il cast float. Aprire site-packages\xlrd\sheet.py e scendere alla linea 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))
...

Avviso cast float -. Si potrebbe provare a cambiare quello ad un decimal.Decimal e vedere cosa succede

EDIT:. Azzerato mia precedente risposta b / c non funzionava correttamente

Sono su Python 2.6.5 e questo funziona per me:

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

Nota: questo solo converte in una stringa. Avrai bisogno di convertire in Decimal te più tardi, se necessario.

Come è già stato detto, un galleggiante non è precisa affatto -. Preservando precisione può essere fuorviante

Ecco un modo per ottenere ogni ultimo bit di informazioni da un oggetto galleggiante:

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

Un altro modo sarebbe in questo modo.

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

Entrambe le corde rappresentano il contenuto esatto del galleggiante. Dietro quasi nulla interpreta altro il galleggiante come pitone pensa probabilmente è stato destinato (che il più delle volte è corretto).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top