5

仮数の最下位ビットがランダムな値に設定されている場合、浮動小数点演算にどのような影響が生じる可能性がありますか?

説明:

PicoLisp言語は、2 つの「機械語」からなるセルという 1 つの構造体を使用してすべての値を割り当てます。32 ビット システムでは、これはセルが 2 つの 32 ビット ポインターまたは整数の 8 バイト構造であることを意味します。セルはそのサイズに合わせて調整されます。つまり、ワードの少なくとも下位 3 ビットは、タイプおよび GC タグ データとして自由に使用できます。

PicoLisp は非常にミニマリストです。この言語に欠けている (多くの) ことの 1 つは、浮動小数点数のサポートがまったくないことです。代わりに、ドキュメントで「スケーリングされた固定小数点」表現と呼ばれるものに完全に依存しています。浮動小数点サポートを追加してみるのは楽しいだろうと思いました。

32 ビット システムでは、64 ビット float は 1 つのセル内にきれいに収まります。これは、割り当てシステムがほとんど同じであることを意味しますが、1 つの小さな問題を除いて、double によってすべての 64 ビットが使用されることを意味します。しかし、GC はビット 0 を GC マーク ビットとして使用することを想定しています。単純に進めると、double に実際に格納された値に関係なく、収集サイクルごとにビット 0 がゼロに設定されます。

(これは、サイズとエンディアンがすべて正しく並んでいると仮定しています。この目的のために、そうであると仮定します。そうでない場合、質問全体は完全に無関係であり、必然的に別の戦略を使用する必要があります。)

では、ハードウェアの浮動小数点演算を使用した汎用数学にとって、これはどの程度の問題なのでしょうか?

double の精度を少し下げるだけであれば、実際には問題ではないと思います。インタープリターの浮動小数点演算がユーザーが期待するほど正確ではなく、そうすべきであることが文書化されている限り、それは実際には問題ではありません。厳密に正確な動作が必要な場合は、フィックスポイントまたはライブラリなどにフォールバックします。私の直感的な理解では、これは最下位ビットであるため、これが当てはまるはずです(文字列に変換しても表示されません..?)。

一方、浮動小数点は魔法です。この種のビット操作は、実際に数学の有用性や、あらゆる種類の一貫した結果を生成する能力に深刻な影響を与える可能性がありますか?

(私は、アロケータの他のいくつかの実装の可能性を検討しました。私は、この戦略が途方もなく馬鹿げているかどうかに特に興味があります。なぜなら、それが最も簡単で、私は怠け者だからです。)

4

3 に答える 3

2

StilesCrisis が提案しているスキームは、一般的に悪いことと考えられている二重丸めを引き起こします。

私が提案したいもう1つのオプション:

2 512倍の大きさであるかのように、各 PicoLisp float を表示して計算します。つまり、二重の加算と減算はほとんど変更されず、乗算と除算には安価な調整が 1 回必要であり、その他の演算 (ライブラリ呼び出し) には 2 回の調整が必要です。

各操作の後、オーバーフローをチェックします (バイアスされた結果が 1.0 を超えるたびに、より頻繁に発生するようになりました)。

これを行うと、仮数の最下位ビットを借りるのではなく、実際には指数の最上位ビットを借りることになります。これには、float を読み込んで格納するためのビット シャッフルが必要ですが、システムを使用するプログラマーに説明するのははるかに簡単です。また、IEEE 754 のようなプロパティ用に設計されたアルゴリズムは引き続き機能します (オーバーフローする場合を除く)。


コードは、軽くテストされた実装のようになります。別のコンテキストでの同様の実装は、このブログ投稿の目的であり、詳細な説明を提供します。

void smalldouble_to_cell(void*p, double d)
{
  union u u;
  u.d = d;
  unsigned long long rest = u.u & 0x7fffffffffffffff;
  unsigned long long packed;
  if (rest > 0x7ff0000000000000)
    /* NaN */
    packed = u.u & 0xfffffffffffffffe;
  else 
    {
      unsigned long long sign = u.u & 0x8000000000000000;
      if (rest >= 0x3ff0000000000000)
    rest = 0x3ff0000000000000;
      packed = sign | (rest << 1);
    }
  memcpy(p, &packed, 8);
}

void double_to_cell(void *p, double d)
{
  smalldouble_to_cell(p, ldexp(d, -512));
}
于 2013-05-09T06:03:53.050 に答える
2

外部コードが常に下位ビットが丸められているかのように値を認識している限り、通常の計算では、仮数を最も近い偶数の値に丸めることによってこれを行います。

つまり、以下で終わる仮数の場合です。

00: 何もしない

10: 何もしない

01: 仮数から 1 を引く

11: 仮数に 1 を追加します (オーバーフローの場合、指数をインクリメントして仮数をクリアする必要があります)。

丸めに一貫性がなく、下位ビットを削除すると、計算にわずかな下方バイアスが発生します。偶数方向への丸めは、IEEE がその下方バイアスに対抗する方法です。

+/- 無限大には注意してください。低いビットを設定すると、それらが NAN に変わり、非常に扱いにくくなります (突然、すべての比較演算が失敗し始めます)。

于 2013-05-09T00:25:06.203 に答える
1

仮数の最下位ビットの変更は、通常、それが持っていると考えているとおりの効果があります。数値の最下位ビットが変更されます。

ただし、いくつかの特殊なケースでは問題が発生します。

[Eric Postpischil の私の以前のテキストに対する正確な批判に従って編集: ゼロ表現を調整すると、非常に小さな非正規数になるだけです]

プラスまたはマイナスの無限大のエンコーディングでは、逆の問題のようなものが見られます。無限大は、可能な限り最大の指数と仮数ゼロでエンコードされます。仮数が変更されると、infinite は NaN に変わります。

于 2013-05-09T00:26:31.993 に答える