2

32 ビットと 64 ビットの浮動小数点値があるとします。

>>> import numpy as np
>>> v32 = np.array([5, 0.1, 2.4, 4.555555555555555, 12345678.92345678635], 
                   dtype=np.float32)
>>> v64 = np.array([5, 0.1, 2.4, 4.555555555555555, 12345678.92345678635], 
                   dtype=np.float64)

これらの値を、精度を失うことなく (または、少なくとも精度を失わないように) テキストにシリアル化したいと考えています。これを行う標準的な方法は次のとおりだと思いますrepr

>>> map(repr, v32)
['5.0', '0.1', '2.4000001', '4.5555553', '12345679.0']
>>> map(repr, v64)
['5.0', '0.10000000000000001', '2.3999999999999999', '4.5555555555555554', 
 '12345678.923456786']

しかし、ファイルサイズを最小限に抑えるために表現をできるだけコンパクトにしたいので、2.4 のような値が余分な小数なしでシリアル化されるといいでしょう。はい、それが実際の浮動小数点表現であることは知ってい%gますが、これを処理できるようです:

>>> ('%.7g ' * len(v32)) % tuple(v32)
'5 0.1 2.4 4.555555 1.234568e+07 '
>>> ('%.16g ' * len(v32)) % tuple(v64)
'5 0.1 2.4 4.555555555555555 12345678.92345679 '

私の質問は%g、このように使用しても安全ですか? 精度が失われないように、正しい値はあります.7か?.16

4

3 に答える 3

7

Python 2.7 以降には、repr0.1 を として出力する float のスマートな実装が既にあり0.1ます。簡潔な出力は、Python に読み戻されたときに正確に同じ浮動小数点値に往復する特定の数値0.10000000000000001の最短表現であるため、他の候補よりも優先して選択されます。このアルゴリズムを使用するには、64 ビット float を実際の Python float に変換してから、次のように渡します。repr

>>> map(repr, map(float, v64))
['5.0', '0.1', '2.4', '4.555555555555555', '12345678.923456786']

驚くべきことに、結果は自然に見え数値的にも正確です。2.7/3.2 の詳細についてはreprWhat's NewとMark Dickinson による興味深い講義を参照してください。

残念ながら、このトリックは、少なくとも Python 2.7 のrepr.

于 2012-09-19T20:54:23.497 に答える
6

IEEE-754 形式の単精度 (32 ビット) 浮動小数点数を一意に決定するには、10 進数の 9 桁 (有意、つまり、値が 0 でない限り 0 から始まらない) を使用する必要があり、9 桁を使用する必要があります。常に十分です。

倍精度 (64 ビット) 浮動小数点数の場合、10 進数で 17 (有効) 桁が必要になる場合があり、常に十分です。

形式がどのように指定されているのかよくわかりません%g。見た目からすると、表現を 0 (0.1) で開始できるため、精度の安全な値は と に.9なり.17ます。

ファイル サイズを最小限に抑えたい場合は、バイト表現を記述すると、はるかに小さいファイルが生成されるため、それが可能であれば、それが適しています。

于 2012-09-19T20:17:16.777 に答える
1

2.7 で派手な repr を実装する C コードは、大部分が Python/dtoa.c にあります (ラッパーは Python/pystrtod.c と Objects/floatobject.c にあります)。特に、_Py_dg_dtoa を見てください。このコードを借用して、double ではなく float で動作するように変更することが可能です。次に、これを拡張モジュールにラップするか、単に so としてビルドして ctypes することができます。

また、ソースによると、実装は「How to Print Floating-Point Numbers Accurately」 by Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 112-126] であることに注意してください。 ." そのため、その論文を読むことで、柔軟性が低く単純なものを自分で実装できる可能性があります (dtoa.c コメントに記載されている変更のうち、適切と思われるものはどれでも)。

最後に、このコードは、AT&T の David Gay によって投稿されたコードへの小さな変更であり、他の多くのライブラリ (NSPR など) で使用されています。

ただし、それを実行する前に、Python 関数を試して、遅すぎるかどうかを測定して、実際にパフォーマンスの問題があることを確認してください。

そして、これが本当にパフォーマンスが重要な領域である場合は、最初からリストをループして repr (または独自の派手な C 関数) を呼び出したくないでしょう。おそらく、float または double の numpy 配列を一度に文字列に変換する関数が必要です。(理想的には、もちろんそれを numpy に組み込みたいと思うでしょう。)

最後にもう 1 つ考えてみましょう。「精度を失わないことに少なくとも本当に近い」ことを探しているのです。double に変換して repr を使用するだけで目的に十分近く、明らかに他の何よりもはるかに簡単であると考えられるため、少なくともテストして除外する必要があります。

言うまでもなく、%.9g%.17gが目的に十分近いかどうかもテストする必要があります。

于 2012-09-19T21:20:54.163 に答える