Safari/Mobile Safari(iOS) にクロージャを使った計算で桁溢れするバグ

前回のエントリ JavaScriptのfor/forEach/jQuery.each/Ext.each のパフォーマンスを計ってみた。の検証中に発見したバグの話し。

Safari 6.0.4 (8536.29.13) 及び、Mobile Safari(iOS 6.1.4)では、クロージャを使った計算では、桁溢れする可能性があります。前回検証に使ったスクリプト http://jsfiddle.net/dsuket/DR33Q/ の、nativeForEach 関数などがその対象です。

    var nativeForEach = function() {
        var sum = 0;
        list.forEach(function(item){
            sum += item;
        });
        return sum;
    }

上記スクリプトをループ回数 1,000,000回以上にした場合、桁あふれが起きて計算結果が正しくでません。

WebKitのバグレポートにもありました。
Bug 104967 - Summary: javascript integer overflow

どうやらJITコンパイラの最適化のバグのようです。JavaScriptの仕様的には Number は IEEE754標準の64ビット浮動小数点数形式で、表現可能な最大値は±1.7976931348623157×10308 です。しかし、パフォーマンスアップのため小さい数の場合はJITが int32として計算するようです。大きくなるとint64となるはずなのですが、クロージャを使った場合うまくいかないという問題です。そのため32bitの範囲(+2147483647〜-2147483647)を超えると桁溢れを起こします。

なお、ChromeFireFoxではもちろん期待通り動作します。SafariでもWebインスペクタを開いていると、この現象は起きない(ちゃんと計算できている)ためデバッグ中は気がつきにくい。。。

上記のバグレポートでは半年も前に RESOLVED FIXED になっているのですが、まだ Safari/Mobile Safariには反映されていません。普段 Safari をメインに使うことはあまりありませんが、iOSの場合は注意が必要ですね。

実はこのような話は昔からあるようです。