2016-08-16 39 views
5

float16ナンシー・ナンバーで数学演算を実行すると、結果もfloat16タイプ番号になります。 私の質問は、結果がどのくらい正確に計算されるかです。 2つのfloat16の数値を乗算/加算すると、pythonは結果をfloat32で生成し、結果をfloat16に切り捨てるか丸めますか?あるいは、「16bitマルチプレクサ/加算器ハードウェア」で計算を行っていますか?Python numpy float16データ型操作、およびfloat8?

別の質問 - のfloat8型がありますか?私はこれを見つけることができませんでした...もしそうでなければ、なぜですか?皆さん、ありがとうございました!

答えて

7
最初の質問に

:(少なくともGPU外)典型的なプロセッサ上float16に対するハードウェアサポートはありません。 numpyのは、あなたがお勧め正確に何を行います。当時float16からfloat32結果ラウンド、float32値にスカラ演算を実行し、float32float16オペランドを変換します。結果はまだ正確に丸められていることが証明できます。float32の精度は、少なくとも4つの基本的な算術演算と平方根の場合、二重丸めが問題にならないほど十分に(float16のものに対して)十分です。現在のnumpyのソースで

、これは、4つの基本的な算術演算の定義はfloat16スカラ演算のためにどのように見えるかです。

#define half_ctype_add(a, b, outp) *(outp) = \ 
     npy_float_to_half(npy_half_to_float(a) + npy_half_to_float(b)) 
#define half_ctype_subtract(a, b, outp) *(outp) = \ 
     npy_float_to_half(npy_half_to_float(a) - npy_half_to_float(b)) 
#define half_ctype_multiply(a, b, outp) *(outp) = \ 
     npy_float_to_half(npy_half_to_float(a) * npy_half_to_float(b)) 
#define half_ctype_divide(a, b, outp) *(outp) = \ 
     npy_float_to_half(npy_half_to_float(a)/npy_half_to_float(b)) 

上記のコードは、NumPyソースのscalarmath.c.srcです。 array ufuncsの対応するコードについては、loops.c.srcを参照することもできます。 npy_half_to_floatおよびnpy_float_to_half関数は、float16タイプのさまざまな他のサポート関数とともに、halffloat.cで定義されています。いいえ、numpyのにはfloat8タイプがありません:2番目の質問については

float16は、標準化されたタイプ(IEEE 754規格に記載されています)です。これはすでに一部のコンテキスト(特にGPU)で広く使用されています。 IEEE 754 float8タイプはなく、 "標準" float8タイプの明白な候補者としては見えません。 NumPyにはfloat8のサポートがあまり必要ないと私は推測しています。

+0

これは、Numpy float16の計算結果が、float16ハードウェアで計算された場合とまったく同じではないことを意味しますか? これは間違いありませんか? – JonyK

+0

@ JonyK:いいえ、全く反対です。 :-) 4つの基本的な算術演算と平方根については、結果は、IEEE 754準拠のハードウェアによってfloat16形式で直接計算された場合と同じ*正確に*同じでなければなりません(少なくとも、丸めモード)。これは完全にはっきりしているわけではありません。各演算に対して短い証明が必要です。float32の精度はfloat16の精度の2倍を超えていますが、それは本当です。 (対照的に、80ビットx87の拡張精度型をfloat64 *と組み合わせると、二重丸めの問題につながる可能性があります。) –

+0

@JonyK:上記の証明のスケッチを追加するために、 。その間に、「何が重複しないか」というGoogleの検索を試してみてください。 –

1

この答えは質問のfloat8側面に基づいています。受け入れられた答えは残りの部分をかなりうまくカバーしています.1つの主な理由の1つに、広く受け入れられていないfloat8タイプがありますが、標準の欠如以外は実用的ではありません。標準的な記法で浮動小数点

プライマーは、float[n]データ型はメモリ内nビットを使用して格納されます。これは、たかだか2^nの唯一の値しか表現できないことを意味します。 IEEE 754では、nanのようなこれらの可能な値のほんの一例が偶数ではありません。つまり、すべての浮動小数点表現(あなたがfloat256になっても)は、有理数の集合にギャップがあり、このギャップの数値を表現しようとすると最も近い値に丸めます。一般に、nが高いほど、これらの隙間は小さい。

float32番号のバイナリ表現を取得するためにstructパッケージを使用すると、実際にギャップが生じることがあります。その最初の時に実行するために少し驚くべきしかし、32のギャップがちょうど整数空間にあります:

import struct 

billion_as_float32 = struct.pack('f', 1000000000 + i) 
for i in range(32): 
    billion_as_float32 == struct.pack('f', 1000000001 + i) // True 

一般的に、浮動小数点は、あなたの番号が同じスケールを持っている場合はそのように最上位ビットのみを追跡することで、最高です、重要な違いは保存されています。浮動小数点標準は、一般に、使用可能なビットをベースと指数の間で分配する方法のみが異なります。例えば、IEEE 754 float32は、ベースに24ビット、指数に8ビットを使用します。

戻るfloat8

に上記ロジックによって、float8値は今までに関係なく、あなたがベースと指数の間にビットを分割しているどのように巧妙な、256の異なる値を取ることはできません。あなたがそれを熱望していない限り、0に近いクラスタ化された256個の任意の数のうちの1つに数値を丸めると、おそらくint8の256の可能性を追跡するより効率的です。

たとえば、粗精度で非常に小さな範囲をトラッキングする場合は、必要な範囲を256ポイントに分割してから、最も近い256ポイントのうちのどれを保存するかを選択できます。あなたが本当に気に入らなければならないのであれば、最も重要なことに依存して、中央または端でクラスタ化された値の非線形分布を持つことができます。

他の誰の可能性(偶数または自分後で)この正確なスキームは極めて小さく、代わりにfloat16またはfloat32を使用するためのペナルティはに小さすぎるとあなたが支払う余分なバイトまたは3時間のほとんどである必要が意味のある違いを作りなさい。したがって、誰もほとんどfloat8の実装を記述するのは苦労します。

関連する問題