知っているつもりだったのに思わずハマってしまった、ActionScript 3 の小数点のワナ。
Number 型数値を乗算して int 型にしていると、「Error #1502: スクリプトがデフォルトのタイムアウト時間の 15 秒を超えて実行されました。」になったりならなかったり。
調べてみると Actionscript での小数点計算の精度に関係していることが分かった。
そういえばそんなことあったよネ、と記憶がよみがえってきた。
function numToInt(n:Number):Array { var p:uint = 0; while (!(n is int)) { n *= 10; p++; } return ([n,p]); } numToInt(1.1); // 11,1 numToInt(1.11); // タイムアウト |
!(n is int)
の判定部分がいつまでも false
のままなのが「タイムアウト」の原因。
実際は、それ以前の処理もあり何度も自分のコードを見直したけど解明できず、やっと思い出したのが「Flash / Javascript って小数点計算はかなり怪しい」だった。
試してみた。
trace( 1.1 * 10 ); // 11 trace( 1.11 * 10 ); // 11.100000000000001 trace(21.3 / 3.0); // 7.1000000000000005 this.target_mc.alpha =0; var k:Number = 0; for(var i:int = 0; i < 10; i++) { k += 0.1; target_mc.alpha += 0.1; } trace(k); // 0.9999999999999999 trace(target_mc.alpha); // 0.9765625 |
当然、Math.cos, Math.tan
なんかの三角関数系にも影響しているのでしょう。
なんか不安になってきた。
小数点がつかない結果だと分っていれば toFixed(0)
すれば良いけど、
除算みたいに答えに小数点がつくかもしれないと困る?
運用でなんとかできるのか?
ちっちゃいことは気にしない!でいいのか?
ちゃんと計算してくれたら悩まなくてすむのに・・・
update
「デキナイ」は言い過ぎだった。
「イケテナイ」に訂正。
2009.04.19 11:22
これは、浮動小数点数が2を底にしていることに由来する挙動ですから、「イケテナイ」のは numToInt() の実装のように思えます(JavsScrip/ActionScriptに限った問題ではありません)。
循環小数のことも考えると、精度(=仮数部 n の桁数)に上限を設けるのが妥当ではないでしょうか?
#Cの frexp() がJS/ASにあれば、もう少しエレガントな実装が出来るんですが・・・