第 4 回 FLOSS 桜山勉強会2007年08月14日 15時05分

FLOSS 桜山第 4 回勉強会 (ページが見られない場合は、アドレスバーに直に URI を入力するなどしてください) にて、主に SpiderMonkey に関する話などをしてきました。資料は以下です。資料完成時点で開始まで 1 時間を切っていたという急ごしらえのもので、タイトルも一時的につけたのを直す間もなく、プレゼンツールにいたっては Firefox Developers Conference のものをそのまま流用しました。

発表時に参考として挙げたページは以下のとおりです。

また、それ以外にも以下のページが参考になるかと思います。

JavaScript で n 進数を扱う2007年08月17日 18時01分

2 進数や 16 進数を使いたいというとき、JavaScript では組み込みの機能を利用できます。使えるのは 16 進数だけではなく、2 進数から 36 進数 (0 ~ 9 および a ~ z を使用) まで扱えます。

n 進数文字列から数値への変換

n 進数文字列から数値へと変換するときは、parseInt 関数を使います。第 2 引数に基数 n を指定することで、第 1 引数の文字列を n 進数であると解釈してくれます。n は 32 ビット整数に変換され、その値が 2 未満または 36 を超えるときは NaN が返ります。ただし、n が 0 になるときは文字列が 10 進数表記であるとして解釈されます。

parseInt(10, 36); // 36
parseInt("10", 0x100000000 + 36); // 36

基数が明示されておらず、文字列が 0 から始まっていた場合、ECMAScript では 8 進数として解釈することを認めつつも 10 進数として解釈することを推奨しています。一方、この場合 JavaScript では、8 進数として解釈することとした上で、それを非推奨であるとしています。

parseFloat 関数には基数を指定することができず、小数部を保持したまま n 進数表記の小数を数値化することはできません。

余談ですが、anarachy golf banner での 2007 年 3 月 8 日時点での JavaScript の記録 (278 バイト) では、データサイズを減らすため数値データを 36 進数文字列として格納し、parseInt を使ってそれを数値化しています。

数値から n 進数文字列への変換

toString メソッドの引数に基数を指定することで、数値を n 進数表記の文字列に変換できます。ECMAScript では、10 を除く 2 ~ 36 の整数が基数として指定されたときの動作は実装依存、その他の値が指定された時の動作は未定義となっていますが、多くの実装では整数の 2 ~ 36 進数表現への変換に対応しています。小数の変換に対応している実装もあります。

10 .toString(16); // "a"
2..toString(2); // "10" ("2." が数値リテラルとして解析される)

各ブラウザの実装

各ブラウザにおける Number#toString の実装状況一覧を作成しました。表から、各ブラウザの実装は以下のようになっているものと推測されます。

IE 7 (JScript)
  • 基数を 32 ビット整数に変換する。
  • 基数が範囲外の場合、例外を投げる。
  • 小数部も n 進数表記に変換する。
Firefox 2.0.0.6 (SpiderMonkey)
  • 基数を 32 ビット整数に変換する。
  • 基数が範囲外の場合、例外を投げる。
  • 小数部も n 進数表記に変換する。
Opera 9.23
  • 基数を 32 ビット整数に変換する。
  • 基数が範囲外の場合、基数として 10 を用いる。
  • 変換する数に小数部が含まれる場合、基数として 10 を用いる。
Safari 3.0.3 Beta (JavaScriptCore)
  • 基数を整数に変換する。
  • 基数が範囲外の場合、基数として 10 を用いる。
  • 小数部も n 進数表記に変換する。
Safari 2.0.4 (JavaScriptCore)
  • 基数を整数に変換する。
  • 基数が範囲外の場合、基数として 10 を用いる。
  • 基数が 10 を除く 2 ~ 36 の整数の場合、変換する数を 32 ビット符号なし整数に変換する。

これを利用すると、Firefox では、(Number.MAX_VALUE * Math.random()).toString(36) とすることにより、大体の確率で 197 ~ 199 桁くらいのランダムな英数字列を生成することができます。ただし、IE で同様のことを行うと、m.plkf48n2dcw(e+197) といった指数表記の文字列が返ってきます。この方法で得られる文字列には偏りがあります

参考

36 進数と文字の偏り2007年08月22日 03時15分

36 進数表記に変換することによるランダムな文字列生成は偏りが大きいよという話。なるほど。というわけで JavaScript の場合を調べてみた。といっても統計学的な検証とかよくわからないのでざっと見た感じで。

Math.random().toString(36)
整数部は当然 0 のはずなので小数部だけ。Firefox は最後の桁に偏りが見られる。IE はすべての桁がそれなりにばらけている。逆に言えば最後の桁が 0 ということも。Safari は後半部分の偏りが大きい。最後の桁は 9 の倍数に大きく偏っている。
Math.floor(Number.MAX_VALUE * Math.random()).toString(36)
Firefox は前後 2 桁に偏りが見られる。最後の桁は 4 の倍数しか表れない。Opera、Safari も均等にばらけてくるのは 3 桁目から。中盤移行は 4 の倍数しか表れない。

Firefox での偏りが相対的に小さいのは、SpiderMonkey では Bigint という独自の構造体を用いて n 進数表記に変換してるからみたいだ。一方 JavaScriptCore では double 型と fmod を用いて変換を行っている

その違いがどうしてこのような結果をもたらすかについては教えてエロい人だが、とりあえず一様性を求めるなら Math.random().toString(36).substring(2, 10) などとしたほうがいいようだ。