[解決済み] double 型を 32bit int 型に丸める高速な方法について説明します。
質問
読むとき
ルア
のソースコードを見ていて気づいたのですが、Lua はマクロを使って
double
の値を32ビットの
int
の値を指定します。このマクロは
Llimits.h
ヘッダーファイル
で、次のように読みます。
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
に従って定義されます。
エンディアン
: リトルエンディアンは0、ビッグエンディアンアーキテクチャは1です。そのため
t
のような整数型に置き換わります。
int
または
unsigned int
.
少し調べてみると、そのマクロには同じテクニックを使ったよりシンプルな形式があることがわかりました。
#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 (つまり、今日ほとんどすべてのマシンがそうである)。正の数でも負の数でも使えますし、丸め方も次のようになります。 バンカーズルール . (これはIEEE754に準拠しているので、驚くことではありません)。
ちょっとしたプログラムを書いてテストしてみました。
int main()
{
double d = -12345678.9;
int i;
double2int(i, d)
printf("%d\n", i);
return 0;
}
そして、次のように出力されます。
-12345679
というのは、予想通りです。
このトリッキーなマクロがどのように働くのか、詳しく理解したいと思います。マジックナンバー
6755399441055744.0
は、実は2
51
+ 2
52
または1.5×2
52
であり、2進数で1.5は1.1と表現できる。このマジックナンバーに任意の32ビット整数を足すと...。
さて、ここからは迷うところだ。 この仕掛けはどうなっているのでしょうか?
更新情報
-
Mysticial が指摘するように、このメソッドは 32 ビットの
int
に拡張することができます。int
の範囲内であれば、2個までとなります。 52 . (マクロに若干の修正が必要ですが)。 -
にはこの方法は使えないという資料もあります。 Direct3D .
-
x86用のMicrosoftアセンブラで作業する場合、アセンブリコードで書かれたさらに高速なマクロがあります(以下もLuaのソースから抜粋しています)。
#define double2int(i,n) __asm {__asm fld n __asm fistp i}
-
単精度の数値にも同様のマジックナンバーがあります。1.5 × 2 23 .
解決方法は?
の値は
double
浮動小数点型はこのように表現されます。
<イグ
で、これは2つの32ビット整数と見なすことができます。
int
あなたのコードのすべてのバージョンで取られた(仮にそれが32ビットの
int
は図の右側なので、結局やっていることは仮数の下位32ビットを取っているだけなんですね。
さて、マジックナンバーですが、あなたが正しく述べたように、6755399441055744は、2
51
+ 2
52
このような数を加えると、強制的に
double
の間の "スイートレンジ "に入るようにします。
52
と2
53
というように
ウィキペディアの説明
という興味深い性質を持っています。
2の間 52 = 4,503,599,627,370,496 と 2 である。 53 = 9,007,199,254,740,992 となり、表現可能な数はまさに整数である。
これは、仮数の幅が52ビットであることから導かれる。
もう一つの興味深い事実は、2の足し算についてです。 51 + 2 52 仮数に影響を与えるのは上位2ビットだけで、これは下位32ビットだけを取り出すので、どのみち捨てられます。
最後になりますが、符号です。
IEEE754の浮動小数点は大きさと符号の表現を使いますが、「普通の」マシンの整数は2の補数演算を使いますが、ここではどのように扱われているのでしょうか?
正の整数のみについて説明しましたが、今度は32ビットの
int
よりも小さいので、(絶対値で)(-2)
31
+ 1)、これを-aと呼ぶ。このような数は,マジックナンバーを加えることで明らかに正になり,その結果得られる値は2
52
+ 2
51
+ (-a).
さて、仮数を2の補数表現で解釈すると、どうなるのでしょうか。それは、(2)の2の補数和の結果でなければなりません。 52 + 2 51 )と(-a)を比較します。ここでも、最初の項は上位2ビットだけに影響し、ビット0〜50に残るのは(-a)の2の補数表現です(ここでも上位2ビットを除いたものです)。
2の補数の幅を小さくするには、左側の余分なビットを切り離せばよいので、下位32ビットを取れば、32ビット、2の補数演算で正しく(-a)が得られます。
関連
-
[解決済み】getline()が何らかの入力の後に使用されると動作しない 【重複あり
-
[解決済み】識別子 "string "は未定義?
-
[解決済み】C++でユーザー入力を待つ【重複あり
-
[解決済み】「corrupted size vs. prev_size」glibc エラーを理解する。
-
[解決済み] 式はクラス型を持つ必要があります。
-
[解決済み】#include<iostream>は存在するのですが、「識別子 "cout "は未定義です」というエラーが出ます。なぜですか?
-
[解決済み】C++の余分な資格エラー
-
[解決済み] gdbを使用してもデバッグシンボルが見つからない
-
[解決済み】C++ - 適切なデフォルトコンストラクタがない [重複]。
-
[解決済み] 変数サイズのオブジェクトが初期化されないことがある c++
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】構造体のベクター初期化について
-
[解決済み】C++でランダムな2倍数を生成する
-
[解決済み] string does not name a type Errorが発生するのはなぜですか?
-
[解決済み】抽象クラス型の無効なnew-expression
-
[解決済み] 非静的データメンバの無効な使用
-
[解決済み】エラー:不完全な型へのメンバーアクセス:前方宣言の
-
[解決済み] [Solved] インクルードファイルが開けません。'stdio.h' - Visual Studio Community 2017 - C++ Error
-
[解決済み】システムが指定されたファイルを見つけられませんでした。
-
[解決済み】std::cin.getline( ) vs. std::cin
-
[解決済み】c++で.txtファイルから2次元の配列に読み込む