byte 型の最大値はなぜ 127 ?

「byte 型は 8 ビットの符号付整数であり,-128~127 の値をとる.」
ここで,なぜ最大値は “128” ではなくて “127” なのでしょうか?
初心者としてはこういうところが非常に気になります.
そして「これも覚えないといけないのか」と割り切れなさを感じてしまいます.
では,どうして “127” までなのか,について調べてみましょう.
ここで「コンピュータは 2 進数しか扱えない」ことを思い出してください.
つまり,”127″ と 10 進数で表示されている数字も,コンピュータの内部では, 2 進数で処理されているというわけです.
すなわち,byte 型が「8 ビットである」ことは「8 桁の 2 進数で表される」ことに他なりません.
「すると 8 ビットで表されるのは 2^8 = 256 なのでは?」
実は,その通りです.
ほら,「-128~127」は丁度 256 個の数字を表現していますね.
(1~127 で 127個,-1~-128 までで 128個,これに 0 の 1個を足して256個です.)
「それなら,byte 型を -127~128 とした方が,正の数が多くていいんじゃない?」
ところが,必ずしもそうとは限りません.
ここで,「8 桁の 2 進数」がどのように 10 進数に対応しているかを見てみましょう.
まず,「正の数か,負の数か」を表すための符号が必要になります.
これを 8 桁のうちの第1桁目とします.
すなわち,正の数は第1桁目を “0” とし,負の数は第1桁目を “1” とします.
正の数 -> 0xxxxxxx (例えば 00011101 など)
負の数 -> 1xxxxxxx (例えば 10110010 など)
こんな具合です.
そうすると,数字は残りの 7 桁で表すことになります.
10 進数の 0 ~ 127 までは次のように,2 進数と対応します.
2 進数 10 進数
0111 1111 127
… …
0000 0010 2
0000 0001 1
0000 0000 0
ここで,8 桁の 2 進数を 4 桁ごとに区切っていますが,これは見やすくするためです.
これは,綺麗に対応していますね.
では次に,負の数 -128~-1 はどのように対応させているのでしょうか?
「1 桁目が符号(±)を表しているのだから,残りの 7 桁が絶対値を表せばいい」
と考えてみるとどうでしょうか,すなわち,
2 進数 10 進数
1111 1111 -127
… …
1000 0010 -2
1000 0001 -1
1000 0000 -0
アレレ,”-0″ なんてモノができてしまいました.
しかも “-128” がありません.
となると 2 進数の “1000 0000” を 10 進数の “-128” を表すものとしましょう.
これらをまとめると次のようになります.
2 進数 10 進数
0111 1111 127
… …
0000 0010 2
0000 0001 1
0000 0000 0
1000 0001 -1
1000 0010 -2
… …
1111 1111 -127
1000 0000 -128
これは美しくありません.
特に “-128” がリズムを乱していますね.では,どうすればイイのでしょうか?
実際には,負の数は次のように対応付けられています.
2 進数 10 進数
1111 1111 -1
1111 1110 -2
… …
1000 0000 -128
すなわち,8 桁の 2 進数 “128~255” を,10 進数の “-128~-1” に対応させているのです.
正の数とあわせて次のようになります.
2 進数 10 進数
0111 1111 127
… …
0000 0010 2
0000 0001 1
0000 0000 0
1111 1111 -1
1111 1110 -2
… …
1000 0000 -128
このような対応付けには次のような利点があります.
1. 加算が自然である.
例えば,10 進数の “-1 + 3” を見てみましょう.
それぞれ 符号付 2 進数に置き換えて計算すると,次のようになります.
1111 1111
+) 0000 0011
1 0000 0010
ここで,9 桁目は無視されますから,答えは “0000 0010 = 2” となります.
先のもうひとつの対応付けでは,こうはいきません.
特に “-1” と “0” のつながりが自然です.
“1111 1111 = -1” に “1” を加えると, “1 0000 0000” になります.
ここで,9 桁目を無視すれば, “0000 0000 = 0” になるのです.
2. 符号付整数を普通の 8 桁の 2 進数とみても,正の数 “0~127” は変わらない.
これが,正の符号を “0” としている根拠です.
ただし,符号付整数 “-128~-1” は “128~255” になってしまいます.
このように,コンピュータの中では 10 進数は 2 進数に置き換えられ処理されています.
この対応付けから,自然と「byte 型は -128~127 の値をとる.」ことが理解できるかと思います.
更に一歩すすんでみましょう.
「byte 型の取りうる範囲は “-128~127″」でした.すなわち “-2^(8 – 1) ~ 2^(8 – 1) -1” ということになります.
これを一般化すると次のようになります.
「n ビットの符号付整数の取りうる範囲は “-2^(n – 1) ~ 2^(n – 1) -1” である.」
JAVA には次の整数型がありますが,実際に調べてみてはいかがでしょうか.
変数型 bit数 範囲
short 16 -32,768 ~ 32,767
int 32 -2,147,483,648 ~ 2,147,483,647
long 64 -9,223,372,036,854,775,808
~ 9,223,372,036,854,775,807
桁が多くなると,何が何だかわからないので整理すると,
int 32 -2 x 10^9 ~ 2 x 10^9 (= 約20億)
long 64 -9 x 10^18 ~ 9 x 10^18
黙々と暗記するよりも,その理由を知ることが,より深い理解につながるのではないかと思います.
そんなことを思い,この文章を書いてみました.