Luaのソースコードを読んだとき、Luaはa macro
を使用してa double
を32ビットに丸めることに気付きましたint
。を抽出するmacro
と、次のようになります。
union i_cast {double d; int i[2]};
#define double2int(i, d, t) \
{volatile union i_cast u; u.d = (d) + 6755399441055744.0; \
(i) = (t)u.i[ENDIANLOC];}
ここでENDIANLOC
は、リトルエンディアン、ビッグエンディアンのエンディアンネスとして定義されています。Luaはエンディアンを慎重に処理します。またはのような整数型を表します。0
1
t
int
unsigned int
私は少し調査しましたがmacro
、同じ考えを使用するより簡単な形式があります。
#define double2int(i, d) \
{double t = ((d) + 6755399441055744.0); i = *((int *)(&t));}
またはC ++スタイルで:
inline int double2int(double d)
{
d += 6755399441055744.0;
return reinterpret_cast<int&>(d);
}
このトリックは、IEEE 754を使用するどのマシンでも機能します(つまり、今日のほとんどすべてのマシンを意味します)。これは正の数と負の数の両方で機能し、丸めはバンカーの規則に従います。(IEEE 754に準拠しているため、これは驚くべきことではありません。)
私はそれをテストする小さなプログラムを書きました:
int main()
{
double d = -12345678.9;
int i;
double2int(i, d)
printf("%d\n", i);
return 0;
}
期待どおりに-12345679を出力します。
このトリッキーなmacro
しくみについて詳しく説明します。マジックナンバー6755399441055744.0
は実際には2^51 + 2^52
、または1.5 * 2^52
であり1.5
、バイナリではと表すことができます1.1
。このマジック番号に32ビット整数が追加されると、まあ、私はここから失われます。このトリックはどのように機能しますか?
PS:これはLuaのソースコードLlimits.hにあります。
更新:
- @Mysticialが指摘するように、このメソッドはそれ自体を32ビット
int
に制限せずint
、数値が2 ^ 52の範囲内にある限り、64ビットに拡張することもできます。(にmacro
はいくつかの変更が必要です。) - 一部の資料では、この方法はDirect3Dでは使用できないとしています。
x86用のMicrosoftアセンブラーを使用すると、さらに高速に
macro
記述されますassembly
(これもLuaソースから抽出されます)。#define double2int(i,n) __asm {__asm fld n __asm fistp i}
単精度数にも同様のマジックナンバーがあります。
1.5 * 2 ^23
ftoi
。しかし、SSEについて話している場合は、単一の命令を使用しないのはなぜCVTTSD2SI
ですか?
double -> int64
は確かに2^52
範囲内です。これらは、浮動小数点FFTを使用して整数たたみ込みを実行する場合に特に一般的です。