割り算の代わりに右ビットシフトを使う、の巻。
1.とりあえず
本当に二次狼の書く文章が役に立つのか。バグるんじゃないのか。むしろ嘘八百言ってるんじゃないのか。
そんな疑いの心を忘れないでください。
と言うかミスがあったら教えてください。
2.じゃ、始めますか
ある程度プログラムをかじっている人は知っていると思いますが、足し算引き算掛け算に比べて割り算は処理が遅いです。
まあ、人間も割り算は他の3演算と比べて時間がかかるはずなので納得はしていただけるのではないかと思います。
というわけで割り算をできるだけ使わないようにすると、軽量化が図れます。
…もっとも、コンピューターは元々計算機です。ぶっちゃけた話、ほとんど速度は変わらないでしょう。
気持ちの問題です(ぉぃ
さて。
この世にはびこるコンピューターには、右ビットシフトと言う計算方法があります。
細かいことは省きますが(適当に検索してください)、要は早く割り算ができます。
ただし、いつもこの計算方法が使えるわけではありません。
1.割られる数が正の数であること。マイナスの値は使えません。
2.割る数が2の累乗であること。2,4,8,16…といった数字ですね。
3.結果は整数になり、小数部分は切り捨てられます。
こんな制限があります。(使用する言語によっては、マイナス値を割ることもできます)
というわけで、あまり使えません(ぉぃ
使い方は、これで、$xに$yを2で割った数が代入されます。
$x = $y>>1;
これを割り算に直すと、
となります。日本語使っちゃいましたが気にしないで。
$x = int($y / (2の1乗)); と書くと、
$x = $y >> $z; と同等の処理が行われます。
$x = int($y / (2の$z乗));
ビットシフトでは小数部分は切り捨てられるのでintで整数化しています。
なお、今回はそんなに関係ありませんが、と書くと、
$x = $y << $z; と同等の処理が行われます。これは左ビットシフトと呼ばれ、掛け算の代わりに使えます。
$x = $y * (2の$z乗);
普通に掛け算をするよりも少しは早いはずですが、ソースが見難くなる割にたいした効果がないので多様はお奨めしません。
…まあ、右ビットシフトも似たようなものですが。
3.こんな風に使います
では、BRIIのソースを使って一つ例を挙げます。
braction.cgiの関数sub ActMoveに、という命令があります。
$go %= 8;
省略しないで書くと、$goを8で割って、余りを返すわけですね。
$go = $go % 8;
この計算とさらに書き換えると、int($go / 8)で、$goを8で割って余りを切り捨てた値が返ります。
$go = $go - int($go / 8)*8;
さらに書き換えましょうビットシフトを使うと、
$go = $go - int( $go / ( 2の3乗) ) * 8; となります。
$go = $go - ($go>>3)*8;
いかがでしょうか?結局、この例ではを利用して割り算を省いた、と言う形になります。
int($go/8) == $go>>3
…あまり良い例ではありませんでしたが、他にいいものが見つからなくて(苦笑)
なお、BRのソースでは「10で割る」と言う命令が多いため、あまり活躍の場はありません。
例えばプレイヤーの現在位置を8進数で扱うようにすると(0でA-1、8でA-2、16でA-3...)、それなりに使えそうです。
やりたくないですが。
4.注意点とか
計算例
4>>2=1
8.5>>2=2
-1>>2=1073741823
8>>2+1=14/4
int(8.5/4)
負の数
先に2+1が計算され、8>>3となる
特に最後の計算例は要注意です。
5.で
困ったことに、私が使っているサーバーは自鯖(他人のだけど)なのでサーバーの処理がすこぶる早く、あんまりこの改造で早くなったような気がしないのですね(ぉぃ
と言うわけで、これを使ってゲームが快適になったら是非報告してください。泣いて喜びます。
6.ちなみに
上に書いた例ですが、これで十分だったりします。細かいことは省きますが、「8で割った余りが返る」と考えてもらって構いません。
$go &= 7;
$goが負の数だったりするとまた問題がややこしくなります。
$go &= 1; //2で割った余り
$go &= 3; //4で割った余り
$go &= 7; //8で割った余り
$go &= 15; //16で割った余り
…といった風にできます。これもビットシフトと同じく、割る側の数は2の累乗限定です。
2006/02/01 追記
2004/01/20 書いた
二次狼