コンピュータの容量の単位であるbyte(バイト)は有名です。しかし、コンピュータの最小の容量はbit(ビット)という単位となります。
今回は、bitとbyteの関連性やプログラミングとの兼ね合いを解説します。この記事は2進数なども出てきます。進数については記事参照のこと。
もくじ
ビット(bit)とバイト(byte)
1バイトは1文字分の容量となります。
但し、1バイトになるのは半角のみ(シングルバイト文字)です。全角文字は2バイト以上(マルチバイト文字)となります。
コンピュータは今でこそ画像や音声、映像などを扱いますが、昔は文字データを保存することが多かったため、文字を基準とした容量となっています。
コンピュータの容量に関する最小単位はビットです。1ビットは2進数の1桁を表しており、0と1の2通りの情報を持つことができます。
バイトとビットは8ビット=1バイトとなります。
文字とbyteとbit
1文字(つまり1バイト)が8ビット必要な理由を、詳細な部分は省いて説明します。
(詳細はwikiを参照してください)
文字(この説明では日本語などの全角は除く)はabcなどのアルファベットや、0から9などの数値、$%などの記号があります。
一つ一つの文字には区別するための番号が割り振られていて、この番号を「文字コード」と呼びます。文字コードには複数の種類があり、種類によって番号は異なります。(ASCIIやShift_JISなど)
以下は文字と対応するASCIIでのコード例
文字 | ASCIIでのコード(10進数) |
---|---|
A | 65 |
B | 66 |
a | 97 |
b | 98 |
@ | 64 |
1ビットは2進数の1桁分となり、2通りの文字しか表現できません。8ビットだと2の8乗(=256)通りの文字が表現できるようになります。
容量と変数と型
JavaやCに代表されるプログラミング言語の一部では型と呼ばれる変数の種類があります。型は種類によって容量が違います。
以下はJavaの整数を格納する変数です。
種類 | 容量 | 保存できる範囲 |
---|---|---|
byte | 8bit | -128~127 |
short | 16bit | -32768~32767 |
int | 32bit | -2147483648~2147483647 |
long | 64bit | -9223372036854775808~9223372036854775807 |
変数を宣言すると、メインメモリ上に変数に応じた容量が確保されます。
以下はbyte型の変数を宣言した場合のイメージ図です。
例えば、byte型で宣言すると8bit分の容量が確保されますが、容量が8bitだと以下のように保存できる数値の範囲も決まります。
桁 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
---|---|---|---|---|---|---|---|---|
bit | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
8bitは2進数8桁分の容量となります。2進数で8桁分を保存するので、2の8乗となり0~255までの数値を扱うことができるのですが、負の数(マイナス)を扱うことができません。そこで、負の数も扱えるように以下のように、一番左の桁は符号ビットという正負を表すビットとなっています。
桁 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
---|---|---|---|---|---|---|---|---|
bit | 正負を判別するビット | 数値に関するビット |
代入する数値とビット(正の数)
例えば、55や127をbyte型の変数に代入する場合には以下のようになります。
byte型の変数で55を格納する際のビット
8桁目(符号ビット)は0の場合は「正」、1~7桁目(0110111)で「55」という10進数の数値を保存しています。
桁 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
---|---|---|---|---|---|---|---|---|
55 | 0 | 0 | 1 | 1 | 0 | 1 | 1 | 1 |
byte型の変数で127を格納する際のビット
8桁目(符号ビット)は0の場合は「正」、1~7桁目(1111111)で「127」という10進数の数値を保存しています。
桁 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
---|---|---|---|---|---|---|---|---|
127 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
代入する数値とビット(負の数)
例えば、-127や-73などの負の数を扱う場合には勝手が違います。負の数をbyte型の変数に代入する場合には以下のようになります。
byte型の変数で-127を格納する際のビット
8桁目(符号ビット)は1の場合は「負」、1~7桁目(0000001)で「127」という10進数の数値を保存しています。
桁 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
---|---|---|---|---|---|---|---|---|
-128 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
byte型の変数で-73を格納する際のビット
8桁目(符号ビット)は1の場合は「負」、1~7桁目(0110111)で「73」という10進数の数値を保存しています。
桁 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
---|---|---|---|---|---|---|---|---|
-128 | 1 | 0 | 1 | 1 | 0 | 1 | 1 | 1 |
符号ビットは単純ですが、問題は数値部分のビットです。
10進数の-127は10000001という2進数となり、
10進数の-73は10110111いう2進数となっています。
数値部分(赤色部分)が単純に10進数から2進数に変換した数値ではなくなっています。この数値は2の補数と呼ばれる数値になっています。
まず、byteの最大値である+127で考えてみます。
①+127は以下のような8桁の2進数となります。
桁 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
---|---|---|---|---|---|---|---|---|
+127 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
②+127の8桁2進数の数値を1増やします。すると以下の表の数値となります。
桁 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
---|---|---|---|---|---|---|---|---|
-128 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
符号ビットが1になり負の数となります。しかし、数値部分は、-0となったり-1になったりはせず、-128という数値となります。
2進数「0000000」が10進数の「-128」になるのは補数という表現です。
③さらに-128の8桁2進数の数値を1増やします。すると以下の表の数値となります。
桁 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
---|---|---|---|---|---|---|---|---|
-127 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
10-128に1を足し、-127という数値となります。
さらに、-127に1を足すと-126(10000010)となります。
つまり、-125は(10000011)、-124は(10000100)となります。
④-1の8桁2進数の数値は以下の表のようになります。
桁 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
---|---|---|---|---|---|---|---|---|
-1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
8桁目(符号ビット)は1の場合は「負」、1~7桁目(1111111)で「-1」という10進数の数値を保存しています。
⑤-1の8桁2進数の数値を1増やします。すると以下の表の数値となります。
桁 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
「11111111 + 1」は本来「100000000」ですが、byteの場合は9桁目以上は利用できません。8桁までが利用され、「0」という10進数の数値を保存しています。
容量と型 サンプルプログラム
以下のプログラムを「ByteLoop.java」という名前でworkフォルダ内に保存します。
保存が完了したら、コマンドプロンプトを起動し、コンパイルおよび実行を行ってみましょう。
1 2 3 4 5 6 7 8 9 10 |
class ByteLoop{ public static void main(String[] args){ byte b = 0; //byte型のデータをループして表示 for(int i = 0; i < 300; i++){ System.out.print(++b); System.out.print(" "); } } } |
実行例
C:\work>javac ByteLoop.java
C:\work>java ByteLoop
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
-128 -127 -126 -125 -124 -123 -122 -121 -120 -119 -118 -117 -116 -115 -114 -113 -112 -111 -110 -109 -108 -107 -106 -105 -104 -103 -102 -101 -100 -99 -98 -97 -96 -95 -94 -93 -92 -91 -90 -89 -88 -87 -86
-85 -84 -83 -82 -81 -80 -79 -78 -77 -76 -75 -74 -73 -72 -71 -70 -69 -68 -67 -66 -65 -64 -63 -62 -61 -60 -59 -58 -57 -56 -55 -54 -53 -52 -51 -50 -49 -48 -47 -46 -45 -44 -43 -42 -41 -40 -39 -38 -37 -36
-35 -34 -33 -32 -31 -30 -29 -28 -27 -26 -25 -24 -23 -22 -21 -20 -19 -18 -17 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
※実行例はコンパイルおよび実行時の例を表示しています。
++bのみでループを回しているのにもかかわらず、127から-128へと値が変更されているのが確認できます。
このようにbitは2進数の桁数(byteなら8bit)となり、桁数によって保存できる数値の範囲などが決まります。数値が有効桁数を超える場合、Javaでは最大桁を超えるとマイナスに反転し、最小値を超えるとプラスに反転します。(127⇔-128)
※ちなみに、桁あふれが起こった際の挙動は言語などによって変わります
補足1 マイナスと補数
マイナスは補数という表現方法を使って表します。補数の詳細はwikiを参考にしてください。
つまり、マイナスの際は単純に10進数と2進数を変換できませんので、以下のように変換します。
比較として、10進数での+127を2進数に変換例
64 + 32 + 16 + 8 + 4 + 2 + 1 = 127
(位に1があるところを足し算)
桁 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
---|---|---|---|---|---|---|---|---|
位 | 正負 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
※位は10進数での換算
続いて、10進数での-127を2進数に変換例
①正の数の127を2進数に置き換える(ここまでは正の数と変わらない)
上記に計算式があるので割愛します。
桁 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
---|---|---|---|---|---|---|---|---|
位 | 正負 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
※位は10進数での換算
②①で置き換えた2進数の1を0に、0を1に反転させます。
桁 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
---|---|---|---|---|---|---|---|---|
位 | 正負 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
③②の数値に1を加えると、-127となります。
桁 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
---|---|---|---|---|---|---|---|---|
位 | 正負 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
補足2 型と容量
先ほど、127を超えると-128になると説明しましたが、下記のような記述ではコンパイルエラーとなります。
byte number = 127;
number = number + 1;
※byte + intの演算結果はintとなり、キャストせずにbyteに代入するとコンパイルエラーとなります。
最大値を超過して、マイナスに反転するのは以下のようなパターンです。(一例)
- 変数内部が最大値の時にインクリメントを行う
- 最大値を超えるように複合代入(+=など)を行う
- int型で最大値を超えるような演算を行う
1 2 3 4 5 6 7 8 9 10 11 |
byte b1 = 127; b1++; System.out.println(b1); //-128 short s1 = 32767; s1 += 1; System.out.println(s1); //-32768 int i = 2100000; i = i * i; System.out.println(i); //-931412992 |
型と容量 練習問題
表示される10進数の数値を8桁2進数に変換してみましょう!
例(入力は必ず符号ビットも含めた8桁)
3→00000011、127→01111111
-2→10000001、-128→10000000