Conversione di un Float Python in una stringa senza perdere di precisione
-
28-09-2019 - |
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.
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).