Pythonフロートを精度を失うことなく文字列に変換する
-
28-09-2019 - |
質問
使用するPythonスクリプトを維持しています xlrd
Excelスプレッドシートから値を取得し、それらと一緒にさまざまなことを行います。スプレッドシートの一部のセルは高精度の数字であり、そのようにしておく必要があります。これらのセルの1つの値を取得するとき、 xlrd
私に与えてください float
0.3828746115497402など。
ただし、コードの後半でこの値を文字列に入れる必要があります。どちらかをしています str(value)
また unicode(value)
「0.382887461155」のようなものを返します。要件は、これは受け入れられないと言っています。精度を保存する必要があります。
私はこれまでにいくつかのことを試してみませんでした。最初は文字列のフォーマットを使用することでした:
data = "%.40s" % (value)
data2 = "%.40r" % (value)
しかし、どちらも同じ丸い数字「0.382887461155」を生成します。
インターネット上のSOや他の場所で同様の問題を抱えている人を探し回ったとき、一般的な提案は、 Decimal
クラス。しかし、私はデータの指定方法を変更することはできません(誰かが秘密の方法を知っていない限り xlrd
デシマルを返します)。そして、私がこれをしようとするとき:
data = Decimal(value)
私は取得します TypeError: Cannot convert float to Decimal. First convert the float to a string.
しかし、明らかに私はそれを文字列に変換することはできません、さもなければ私は精度を失います。
ええ、私はどんな提案にもオープンです - 必要に応じて本当にひどく/ハッキーなものでさえ。私はPython(Java/C#Guy自身)をそれほど経験していないので、ここである種の根本的な誤解があれば、自由に修正してください。
編集:Python 2.6.4を使用していると付け加えると思いました。バージョンの変更を妨げる正式な要件はないと思います。他のコードを台無しにする必要はありません。
解決
私はXLRDの著者です。コメントで反論するために他の答えやコメントには非常に多くの混乱があるので、私は答えでそれをやっています。
@katriealex: "" "Xlrdの勇気で失われる精度" "---まったく根拠のない真実ではありません。 XLRDは、XLSファイルに保存されている64ビットフロートを正確に再現します。
@katriealex: "" "地元のXLRDインストールを変更してフロートキャストを変更することが可能かもしれません" "" ---なぜこれをやりたいのかわかりません。 16ビットの整数を浮かべることで、正確なことはありません!!!いずれにせよ、そのコードは、Excel 2.xファイル(整数型セルレコードがある)を読み取るときにのみ使用されます。 OPは、彼がそのような古代のファイルを読んでいることを示すものではありません。
@Jloubert:あなたは間違っている必要があります。 "%.40r" % a_float
同じ答えを得るためのバロック様式の方法です repr(a_float)
.
@everybody:フロートを小数点に変換して精度を維持する必要はありません。の全体のポイント repr()
関数は、以下が保証されていることです。
float(repr(a_float)) == a_float
Python 2.x(x <= 6)は、元の値を再現することが保証されているため、一定の17小数桁の精度を示します。後のPythons(2.7、3.1)は、元の値を再現する小数桁の最小数を与えます。
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
したがって、一番下の行はそれです フロートオブジェクトのすべての精度を保持する文字列が必要な場合は、使用してください。 preserved = repr(the_float_object)
...後で値を回復します float(preserved)
. そんなに簡単です。必要ありません decimal
モジュール。
他のヒント
使用できます repr()
精度を失うことなく文字列に変換するには、小数に変換します。
>>> from decimal import Decimal
>>> f = 0.38288746115497402
>>> d = Decimal(repr(f))
>>> print d
0.38288746115497402
編集:私は間違っています。私はこの答えをここに残しますので、スレッドの残りの部分が理にかなっていますが、それは真実ではありません。上記のJohn Machinの回答をご覧ください。ありがとう=)。
上記の回答が素晴らしい場合、それは素晴らしいことです - それはあなたに多くの厄介なハッキングを救います。しかし、少なくとも私のシステムでは、彼らはそうしません。これをEGで確認できます
import sys
print( "%.30f" % sys.float_info.epsilon )
その数は、システムがゼロと区別できる最小のフロートです。操作を実行すると、フロートからランダムに追加または差し引かれることがあります。 これは、少なくとも私のPythonのセットアップでは、の精度が根性の中で失われることを意味します xlrd
, 、そして、それを変更せずにできることは何もないようです。奇妙です。私はこの事件が以前に発生したと予想していたが、どうやらそうではないだろう!
あなたの地元を変更することが可能かもしれません xlrd
変更するためのインストール float
キャスト。開いてください site-packages\xlrd\sheet.py
そして、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))
...
に注意してください float
キャスト - それをaに変更してみることができます decimal.Decimal
そして、何が起こるか見てみましょう。
編集: 私の以前の回答b/cをクリアして、それは正しく動作しませんでした。
私はPython 2.6.5を使用していますが、これは私のために機能します:
a = 0.38288746115497402
print repr(a)
type(repr(a)) #Says it's a string
注:これは文字列に変換するだけです。に変換する必要があります Decimal
必要に応じて後であなた自身。
すでに言われているように、フロートはまったく正確ではありません。そのため、精度を維持することはやや誤解を招く可能性があります。
フロートオブジェクトからすべての最後の情報を取得する方法は次のとおりです。
>>> from decimal import Decimal
>>> str(Decimal.from_float(0.1))
'0.1000000000000000055511151231257827021181583404541015625'
別の方法はそうです。
>>> 0.1.hex()
'0x1.999999999999ap-4'
両方の文字列は、フロートの正確な内容を表しています。 Pythonがおそらく意図していると考えているので、他のほとんどはフロートを解釈します(ほとんどの場合、これは正しいです)。