2

次のC変換関数がどのように機能するのか(そしてなぜこのように記述されているのか)がわかりません。私は、元の作者が彼が何をしているかを知っていたとかなり確信しています。

typedef union TValue {
  uint64_t u64;
  double n;
  struct {
    uint32_t lo;    /* Lower 32 bits of number. */
    uint32_t hi;    /* Upper 32 bits of number. */
  } u32;
  [...]
} TValue;


static int32_t num2bit(double n)
{
  TValue o;
  o.n = n + 6755399441055744.0;  /* 2^52 + 2^51 */
  return (int32_t)o.u32.lo;
}

static uint64_t num2u64(double n)
{
#ifdef _MSC_VER
  if (n >= 9223372036854775808.0)  /* They think it's a feature. */
    return (uint64_t)(int64_t)(n - 18446744073709551616.0);
  else
#endif
  return (uint64_t)n;
}
  • num2bitは実際ににキャストするだけdoubleですint32_tか?なぜ追加するのですか?なぜこのように書くのですか?
  • num2u64でほのめかされているこの「機能」とは何ですか?(_MSC_VERは、MicrosoftのCコンパイラのコードパスであることを意味すると思います)。

これらの関数は常に使用されるわけではないことに注意してください(CPUアーキテクチャによって異なります)。これはリトルエンディアン用です(単純化するためにいくつかのプリプロセッサマクロを解決しました)。

オンラインで閲覧可能なミラーへのリンク(コードはLuaJITプロジェクトからのものです):サラウンドヘッダーファイル(またはプロジェクト全体)。

すべてのヒントをいただければ幸いです。

4

4 に答える 4

11

num2bit は、 Lua BitOp セマンティクス、特にwrtを実装するように設計されています。モジュラー算術。とにかく、LuaJIT は特定の CPU、プラットフォーム、およびコンパイラに対してのみ機能するため、実装定義の動作は十分に制御されています。このコードを他の場所で使用しないでください。

num2u64 は、int64_t を介して常に double を uint64_t に変換する MSVC のバグ/誤機能の回避策です。これは、>= 2^63 の数値に対して望ましい結果をもたらしません。MS は、この嫌悪感を「機能」と見なしています。当たり前。

于 2013-01-24T10:27:36.310 に答える
3

num2bit: 51 番目と 52 番目のビットを 1 に設定すると、指数が特定の数値になります (そうしないと、オーバーフローが発生します)。(int32_t)o.u32.lo を返すと、整数が返されることがわかります。指数が固定されているため、double の「下位 32 ビット」と同じ値。したがって、これはほとんどの double の整数値をすばやく取得するためのトリックです。こうすることで小数点以下を切り捨ててしまうようで、そもそも2^51以上だと思わぬ効果がありそうです。

>>> math.frexp(1.0 + 6755399441055744.0)
(0.7500000000000001, 53)
>>> math.frexp(0.0 + 6755399441055744.0)
(0.75, 53)
>>> math.frexp(564563465 + 6755399441055744.0)
(0.7500000626791358, 53)
>>> math.frexp(-564563465 + 6755399441055744.0)
(0.7499999373208642, 53)
>>> math.frexp(1.5 + 6755399441055744.0)
(0.7500000000000002, 53)
>>> math.frexp(1.6 + 6755399441055744.0)
(0.7500000000000002, 53)
>>> math.frexp(1.4 + 6755399441055744.0)
(0.7500000000000001, 53)

編集: 51 番目と 52 番目のビットの両方が設定されている理由は、52 番目のビットのみを設定すると、負の数によって指数が変更されるためです。

>>> math.frexp(0 + 4503599627370496.0)
(0.5, 53)
>>> math.frexp(-543635634 + 4503599627370496.0)
(0.9999998792886404, 52)

num2u64: わかりません。ただし、最初の数値は 2^63 で、2 番目の数値は 2^64 です。おそらく、2^63 より大きい倍精度浮動小数点数を整数表現にキャストする際のオーバーフローまたは符号の失敗を防ぐためですが、それ以上は言えません。

于 2013-01-23T21:43:12.997 に答える
1

num2bit最も近い整数への丸めを使用して、IEEE 標準のメモリ内表現doubleを 32 ビット固定小数点の 2 の補数符号付き形式に手動で変換します。

厳密な型エイリアス規則unionに違反するため、 a による変換は安全ではありません。ユニオンの 1 つのメンバーに書き込み、別のメンバーから読み取ることはできません。次のようなことをする方が適切でしょう

static int32_t num2bit(double n)
{
  int32_t o;
  n += 6755399441055744.0;  /* 2^52 + 2^51 */
  memcpy( & o, & n, sizeof o ); /* OK with strict aliasing but must mind endianness. */
  return o;
}

この関数はおそらく最適化を目的としていますが、その価値自体は疑わしいものです。すべての新しいマイクロプロセッサで再テストし、より高速なハードウェアでのみ使用されるようにする必要があります。

単純な C の浮動整数変換では、ゼロへの丸めまたは切り捨てが使用されることにも注意してください。この関数は、おそらく小数の値を処理することを意図したものではありません。


num2u64は Windows 固有の回避策です ( に注意してください#ifdef)。2 63doubleより大きい値を符号なし整数に変換すると、「何か悪いこと」が発生する (おそらく飽和) ため、作成者は 2 64を減算して負の数にし、それを符号付きの負の整数にキャストしてから、結果をキャストします。 2 63より大きい値を持つ符号なし整数に変換します。

いずれにせよ、目的は単純に adoubleを aに変換することであるuint64_tことがわかります。Windows 以外のプラットフォームではそれがすべてであるためです。

于 2013-01-24T02:22:52.513 に答える
0

これらの機能は魔法によって「機能」します。

これは、C 標準ドラフトである n1570.pdf の §6.2.6.1p7 から来ています。他のメンバーに対応し、不特定の値を取る

提示されたコードが、on に割り当ててから o.u32.lo の値を使用することにより、未指定の値を使用する方法に注意してください。

これは、C 標準ドラフトである n1570.pdf の §6.3.1.3p3 から来ています。結果が実装定義であるか、実装定義のシグナルが発生します。

提示されたコードが、符号なしから符号付き 32 ビット整数に複数回変換するときに、実装定義の動作を呼び出す方法に注意してください。代わりに、実装定義の計算例外シグナルを発生させると仮定します。デフォルトのシグナルハンドラが戻った場合、これも未定義の動作になります。/* They think it's a feature. */

于 2013-01-23T22:27:12.673 に答える