ゲームを作っているとアリガチなハマりかた

結論から言うと割り算。


Rubyでは、IntegerとFloatは特に意識しないでもごちゃ混ぜにできるし、勝手に変換してくれる。
座標などは初期値は整数で入れて、移動するときは三角関数を使って実数を足したり引いたりして、そうすると座標情報は実数になる。
加減乗除のうち加減乗をしているだけなら何も意識しなくていいが、除だけは問題だ。
整数を整数で割ると、端数が切り捨てられてしまうから。


固定の数字で割る場合なら、整数を書かずに後ろに.0とかつけておけば大丈夫。
だが、変数を変数で割る場合に、ある条件に限定して両方が整数の時がある、となるとこれは厄介だ。
ソース中に出てくる数字を全部実数にしてしまうのも一つの手だが、それはそれで別のハマりかたをしそうだし、なんかいい手は無いものか。


覚えておいて気をつける、ぐらいしか手は思いつかないので、とりあえず実行速度などを測ってみよう。
Cなんかだと変数に型があるから、計算式を書いた時点で実数か整数かは確定するんだけどな。
このへん動的型付言語ならではの問題なのかもしれない。
とかいいつつ、計測に使ったスクリプトはこんな感じ。

require 'benchmark'

puts Benchmark.realtime {
  for i in 0...1_000_000
    1 * 1
  end
}

実行環境はruby 1.8.7 (2009-06-12 patchlevel 174) [i386-mswin32]。
これを実行すると、

0.75

という感じ。
中身を1.0 / 1.0にすると、

0.9375

になる。当たり前だが整数のほうが速い。
1.0 * 1にすると、

0.953125

となる。
っていうか、0.9375と0.953125の間の数字が出ないので、かなり誤差が大きいと思う。
環境にもよるのだろうが誤差が100ms単位で出るのでは、ゲーム用途での計測には使えない。
HPCモジュールの機能をDXRubyに組み込んでおくか。
まあともかく、整数同士の計算は速く、実数同士になると2割強遅い。
整数→実数の変換は無視できるレベルである。


この結果から考えると、「変換のロスがあるからできるだけ実数で書くべき」とか「必要なければできる限り整数で扱うべき」とか言うほどの差は無い。
普通にコードを書いた場合と、そのへんを細かく考えて最適化した場合とを比べても、ほとんど速くはならないだろう。
ループ10000回の内部とかだと変わってくるだろうけど、普通のコードではそこまでチューニングするほどではない。
できるだけ実数で書くのがよければ割り算は意識する必要なくなるし、できるだけ整数で書くのがよいなら割り算は意識するのが必須になる。
が、普通に書くのがたぶん一番いいのなら、割り算は基本的に意識する必要は無いが時々ハマるということになる。
やっぱりどうにも厄介な話だ。