🎉

コンピュータも計算を間違える?

2022/01/01に公開約1,500字

以下のコードで出力される数字はいくつになるか分かるだろうか。

zenn.java
class Main{
    public static void main(String[] args){
        byte num = 127;
        num++;
        System.out.println(num);
    }
}

128と考える方もいると思うが-128となる。
この事象はコンピュータ内での数値の表し方に原因があるらしい。

コンピュータにおける数値の表し方

コンピュータ内では数値は2進数で管理されている。
例えば3だったら11、16だったら10000といった感じだ。
そして127は8ビットで01111111と表示される。
これに+1したら10000000と表示される。
あれ、10000000って128じゃない?と思われた方もたくさんいるだろう。
ここでトリッキーなのは最上位ビット(10000000の1)の扱いだ。

コンピュータにおける符号の表し方

2進数ではマイナスの数を扱う際に「-」の符号は使わない。
0と1しかないからだ。
その代わり0ならプラス、1ならマイナスと判断する。
例えば8ビットで数値を表す際に1ビット目を符号を、残りの2~8ビットで絶対値を表す。

補数について

前項の続き。
1ビット目で符号を表す。
では-3は3が00000011だから10000011で良いのかと言うとそうではない。
3+(-3)は0となるだろう。では00000011+10000011は0になるだろうか??
ならないだろう。

負の数を表す際には補数という考え方を使う。
補数とはWikipediaによると、

補数(ほすう、complement)とは、ある基数法において、ある自然数 a に足したとき桁が1つ上がる(桁が1つ>増える)数のうち最も小さい数をいう。

つまり10進数だと2の補数は8といった感じだ。
補数を足すと必ず1の桁は0になり、かつコンピュータでは桁上がりを無視するという性質がある。
例えばバイト型の変数は8桁までしか表せないため9桁目はカットされる。
これを踏まえると-3は11111101になる事が理解できるだろう。(00000011+11111101=100000000となり9桁目の1はカットされるため0となる)

8ビットで表すことのできる数値は以下のようになる。

2進数 10進数
00000000 0
00000001 1
00000010 2
00000011 3
---- ----
01111101 125
01111110 126
01111111 127
10000000 -128
10000001 -127
10000010 -126
---- ----
11111101 -3
11111110 -2
11111111 -1
よって10000000は-128となる。

まとめ

rubyを使っていたときはbyte,intなどのバイト数を気にすることはなかったが型付き言語のjavaを扱うことで数値の扱いなどのコンピュータ内部の動きをより気にするようになった。
他にもjavaには抽象クラスやインターフェースなどより柔軟な設計が出来る機能があるのもとても興味深い。
Javaを使って仕事してみたいなぁ。。

Discussion

ログインするとコメントできます