4

タグ付けされたバリアント型を主な値型として使用するプログラミング言語Septemberを開発しています。型 (整数、文字列、オブジェクト、例外など) には 3 ビットが使用され、実際の値 (実際の整数、オブジェクトへのポインターなど) には 61 ビットが使用されます。

まもなく、float言語に型を追加する時が来ます。64 ビット double のスペースがほとんどあるので、内部で double を計算に使用したいと考えました。私は実際にはストレージが 3 ビット不足しているため、各計算の後に double を四捨五入する必要があります。基本的には、仮数または指数が 3 ビット短い 61 ビットの double になります。

しかし!浮動小数点には危険が伴い、紙の上では理にかなっているように思えることを行うと、FP 数学で悲惨な結果が生じる可能性があることを知っているので、専門家に自由回答形式の質問をします。

このアプローチはまったく実行可能ですか? 各ステップで丸めを行うと、実行時間の長い計算で重大なエラー累積の問題が発生する可能性はありますか? それを避けるために丸めを行う特定の方法はありますか? そのように扱うことができない特別な値はありますか (サブノーマルが思い浮かびます)。

理想的には、float がネイティブの 61 ビット double と同じように動作するようにしたいと考えています。

4

2 に答える 2

10

倍精度形式の指数フィールドからビットを借用することをお勧めします。これは、この記事で説明されている方法です (指数から 1 ではなく 3 ビットを借用するように変更します)。このアプローチでは、非常に大きなまたは非常に小さな中間結果を使用しないすべての計算は、元の倍精度計算とまったく同じように動作します。新しい形式の非正規領域に実行される計算でさえ、1+8+52 61 ビット形式が IEEE によって標準化された場合とまったく同じように動作します。

対照的に、単純に仮数から任意の数のビットを借用すると、多くの二重丸めの問題が発生し、52 ビットの仮数から少数のビットのみを削除して仮数に丸めることがますます頻繁になります。質問の編集で提案したように仮数から 1 ビットを借りることは最悪であり、操作の半分が統計的に二重丸められた結果を生成し、理想的な「ネイティブ 61 ビット double」が生成したものとは異なります。これは、0.5ULP の精度ではなく、基本演算が 3/4 ULPの精度になることを意味します。これは、0.5ULP を期待する既存の精巧に設計された数値アルゴリズムの多くを狂わせる劇的な精度の損失です。

ただし、11 しかない指数から借用するビット数は 3 とかなり多く、言語で単精度 32 ビット形式を使用することも検討できます (ホストから単精度演算を呼び出します)。

最後に、ここで Jakub が見つけた別の解決策を可視化します。仮数から 3 ビットを借用し、49 の明示的仮数ビットで最も近い数値に変換する前に、中間倍精度計算の奇数への丸めをシミュレートします。 11 指数ビット形式。この方法を選択した場合、49 ビットの仮数部への丸め自体は次の操作で実現できることに注意してください。

if ((repr & 7) == 4) 
  repr += (repr & 8) >> 1);   /* midpoint case */
else
  repr += 4;
repr &= ~(uint64_t)7; /* round to the nearest */

考慮されているものと同じ表現を持つ整数で作業しているにもかかわらずdouble、上記のスニペットは、数値が正規から非正規、非正規から正規、または正規から無限に変化しても機能します。もちろん、上記のように解放された 3 つのビットにタグを設定する必要があります。ボックス化されていない表現から標準の倍精度数を復元するには、 でタグをクリアするだけrepr &= ~(uint64_t)7;です。

于 2014-04-16T14:43:02.597 に答える