JavaScript (ECMAScript) の整数の精度と乱数の精度について
- 2010-05-30
- カテゴリ: Client Side
- タグ: JavaScript Tips
久しぶりにJavaScript (というよりECMAScript) のネタ。今日はECMAScriptで扱える整数と乱数について。
ECMAScriptの数値 (Number) はIEEE 754の倍精度浮動小数点数であると仕様書に明記されている。
ECMAScript Language Specification 5th edition
4.3.19 Number value
primitive value corresponding to a double-precision 64-bit binary format IEEE 754 value.
(強調は引用者)
ECMAScript Language Specification 3rd edition
4.3.20 Number Type
The type Number is a set of values representing numbers. In ECMAScript, the set of values represents the double-precision 64-bit format IEEE 754 values including the special “Not-a-Number” (NaN) values, positive infinity, and negative infinity.
(強調は引用者)
なので、倍精度IEEE 754値の精度の範囲内であれば、桁落ちの心配をせずに整数として扱うことができる。
Wikipediaの情報等によると、IEEE 754の倍精度浮動小数点数は52ビットの仮数部を持っていて、53ビットの精度で仮数を表現することができるらしい[1]。
つまり、2の53乗 (9,007,199,254,740,992) 未満の整数であれば桁落ちの心配をせずに扱うことができる。本当にそうなのか、簡単なコードを書いて確かめてみた。
準備
まずは53ビット近傍の数値 (二進法表現) を用意。
var bin = [ "11111111111111111111111111111111111111111111111111010", // 2^53 - 6 "11111111111111111111111111111111111111111111111111011", // 2^53 - 5 "11111111111111111111111111111111111111111111111111100", // 2^53 - 4 "11111111111111111111111111111111111111111111111111101", // 2^53 - 3 "11111111111111111111111111111111111111111111111111110", // 2^53 - 2 "11111111111111111111111111111111111111111111111111111", // 2^53 - 1 "100000000000000000000000000000000000000000000000000000", // 2^53 "100000000000000000000000000000000000000000000000000001", // 2^53 + 1 "100000000000000000000000000000000000000000000000000010", // 2^53 + 2 "100000000000000000000000000000000000000000000000000011" // 2^53 + 3 ];
「二進法文字列 → 数値型 → 十進法文字列」テスト
二進法表現の数値を数値型に変換し、十進法文字列として出力してみる。
document.writeln("convert binary string to Number values"); var num = []; for (var i = 0; i < bin.length; i++) { num[i] = parseInt(bin[i], 2); document.writeln(num[i] + " <- " + bin[i]); }
2^53を超えたあたりから十進法表現があやしくなる。
「二進法文字列 → 数値型 → 二進法文字列」テスト
二進法表現を数値型に変換した後、もう一度二進法表現に戻してオリジナルの二進法表現と比較してみる。
document.writeln("compare original and reconverted binary strings") for (var i = 0; i < bin.length; i++) { var b = num[i].toString(2); document.writeln(b + " == " + bin[i] + " is " + (b == bin[i])); }
ここでもやはり2^53を超えたあたりから挙動があやしくなる。
隣り合った数値型同士の比較 (1)
隣り合った数値型同士を比較する。小さいほうの数値をインクリメントし、大きいほうの数値と一致するかどうか。
document.writeln("compare adjoining numbers") for (var i = 1; i < bin.length; i++) { document.writeln(num[i - 1] + " + 1 == " + num[i] + " is " + (num[i - 1] + 1 == num[i])); }
ここでも2^53を超えたあたりから挙動がおかしい。
隣り合った数値型同士の比較 (2)
2^53近傍の数値からインクリメントしていって、大小関係が壊れる数値を探す。
var e = 52.9999999999, n = parseInt(Math.pow(2, e)); window.alert(n + " ~ 2^" + e); while (n < ++n); window.alert((n - 1) + " < " + n + " is false");
9007199254116649 (2^53 - 624343)
からカウントアップしながら隣通しの値を比較していくと、やはり9007199254740992 (2^53)
で大小関係がおかしくなることがわかる。
結論
JavaScript (ECMAScript) では9007199254740992 (2^53)
未満の正整数に関しては桁落ちを心配せずに扱うことができる。
ちなみに、↑のテストではわからないけれど、負整数に関しても同じ精度を持っているので、-2^53 < x < 2^53
の範囲の整数に関しては問題なく扱うことができる[2]。
続く
本当はJavaScriptのMath.random()で扱える乱数の精度について書くつもりだったんだけど、整数の精度の話でだいぶ長くなったので乱数の話はまた次回。
続き
続きを公開しました!
注釈
-
^ 52ビットで53ビットを表現できると聞くと奇妙に聞こえるけれど、けち表現という技を使って1ビット短縮しているだけで、さほど特別なことをしているわけではない。
↓は9ビットの精度が必要な461という整数を、けち表現を使って8ビットの仮数部で表現する例。
111001101. * 2^0 == 461 * 1 11100110.1 * 2^1 == 230.5 * 2 1110011.01 * 2^2 == 115.25 * 4 111001.101 * 2^3 == 57.625 * 8 11100.1101 * 2^4 == 28.8125 * 16 1110.01101 * 2^5 == 14.40625 * 32 111.001101 * 2^6 == 7.203125 * 64 11.1001101 * 2^7 == 3.6015625 * 128 1.11001101 * 2^8 == 1.80078125 * 256 11001101 8 ^ implied (hidden) 1 bit ^^^^^^^^ 8-bit fraction ^ exponent
要は整数部分が1になるように指数部分を調整して、整数部分を省略するということ。1ビット分の情報は指数部を使って上手に表現されている。
-
^ ビット演算子 (
~, <<, >>, >>>, &, ^, |
) に関してはこの限りではない。ビット演算子が適用される前に、数値型は符号付き(または符号無し)32ビット整数に変換される。
関連記事
- 乱数の精度を調べてたらOpera Miniのバグを見つけた
- JavaScriptの乱数の精度の話
- JavaScript (ECMAScript) の整数の精度と乱数の精度について
- [JavaScript] prototypeに直接代入しちゃうのってダメじゃなかったっけ?
- [Windows] NTFSハードリンクで増分スナップショットをとるバックアップツールを作りました!
トラックバック URL
- http://liosk.blog103.fc2.com/tb.php/197-11492399
トラックバック
- JavaScriptの乱数の精度の話
-
前回の記事で予告したとおり、今回はJavaScriptのMath.random()で生成できる乱数の精度の話。
前回の記事で、JavaScriptでは2^53未満の正整数を扱うこと...
- 2010-06-03
- 発信元: LiosK-free Blog