読者です 読者をやめる 読者になる 読者になる

プログラムdeタマゴ

nodamushiの著作物は、文章、画像、プログラムにかかわらず全てUnlicenseです

二つの数の正負が同じかどうかを判定する

二数a,bの正負が一致しているかどうかは数学的には

a*b>=0

で与えられる。

 

が、プログラム的にはこれではうまくいかないことがある。

それはaとbの絶対値がそれなりに大きな値の時だと、オーバーフローを起こしてしまうのだ。

バグってたところがそれが原因だった。

 

で、素直に

(a>=0)==(b>=0)

 

とするよりも、

 

(a&-2147483648)+(b&-2147483648)==0

とした方が速いっぽい。

ちなみに、私の環境で測定した結果、40%近く速い。

修正
(a&-2147483648)==(b&-2147483648)と素直にすればいいじゃんw。でも測定したら速度差はなかったです。たぶん、==0のあたりはJITコンパイルされるときにZeroフラグを見るから消えるんじゃないかな。見やすさ的にはこっちの方が見やすいね。

 

これは、両方の最上位ビットだけ取り出して足し算する、という処理だ。

0を含む正数同士なら0+0で0になるし、負数同士なら1+1でオーバーフローして0になる。

 

なお、!(a>=0 ^ b>=0)はもっと遅かった。

 

 

というわけで、うっかり数学の癖でかけ算するのはやめた方が良いということを痛感した。するときは足し算でやるか、素直にやるかのどっち化にしておくと常に正しい答えが出ます。

 

測定ソース。実行順序の入れ替えもしてみたけど、やっぱりroop2に方が速かった。


public class Test {

    public static void main(String[] args) {
        long t,t2,t3;
        roop();
        roop2();
        roop3();
        roop();
        roop2();
        roop3();
        t=System.nanoTime();
        roop();
        t=System.nanoTime()-t;
        t2=System.nanoTime();
        roop2();
        t2=System.nanoTime()-t2;
        t3=System.nanoTime();
        roop3();
        t3=System.nanoTime()-t3;
        System.out.println(t-t3);
        System.out.println(t2-t3);
        System.out.println( (t-t3)*100/(t2-t3));
    }

    static void roop(){
        boolean b;
        for(int k=0;k<1000;k++)
        for(int a,i=-300;i<1000;i++){
            for(a=0;a<1000;a++){
                b = (a>=0)==(i>=0);
            }
        }
    }

    static void roop2(){
        boolean b;
        for(int k=0;k<1000;k++)
        for(int a,i=-300;i<1000;i++){
            for(a=0;a<1000;a++){
                b = (a&-2147483648)+(i&-2147483648)==0;
            }
        }
    }
    static void roop3(){
        boolean b;
        for(int k=0;k<1000;k++)
        for(int a,i=-300;i<1000;i++){
            for(a=0;a<1000;a++){
            }
        }
    }

}