CPU や メモリ、ストレージ、OS の働きがなんとなく理解できている、くらいの方を対象に、コンピュータのしくみ について書いていきます。
C 言語や Python をメインに扱っていきますが、たまに C# や JavaScript がでてくる予定です。
Indexx
なぜ2進数を扱うのか
結論から行くとコンピューターが 0 か 1 の値しか扱えないためです。
ここでいう 0 と 1 は論理としての 0 と 1 です。コンピューターはこの 0 と 1 を通電状態を利用して表現しています。
- 0 を通電していない状態、あるいは低電圧
- 1 を通電状態、あるいは高電圧状態
として表現します。
2進数の 1桁をビットと呼び、それが 8つ集まった 8 ビットが 1バイトです。

IC(集積回路)
コンピューターは IC (集積回路、Integrated Circuit)によってできています。IC を組み合わせることで CPU や GPU、メモリなどが作られています。

IC によって作られている CPU や GPU 、メモリ内ではこの 0 と 1 を利用してあらゆるデータを表現し、演算しています。
トランジスタ
IC を構成する基本的な要素のひとつが「トランジスタ」です。トランジスタは半導体デバイスであり、電子信号の増幅やスイッチングなどを行うことができます。CPU や GPU、メモリ、SSD などは IC によって作られており、その IC はトランジスタで作られています。つまりこのトランジスタがコンピューター技術を支えている基になっている要素です。

いわゆる半導体技術とは、トランジスタを作る技術を指します。トランジスタは IC を構成し、PC に限らずスマートフォンやテレビ、ゲーム機、ラジオ、自動車、医療機器、家電製品など様々な用途で利用されています。
原理的にはトランジスタ1つで 1ビットの情報を表現します。ただ、SRAM(Static Random Access Memory)では1ビットを格納するのに6つのトランジスタが使われることが多いです。ここではシンプルな DRAM(Dynamic Random Access Memory)の 1ビット 1トランジスタの構成を扱っていきます。

あらゆるコンピューターがの基盤はトランジスタで、トランジスタは ON / OFF の状態、あるいは高電圧と低電圧の状態しか持てません。そのためコンピューターはあらゆるデータを 2進数で表現、演算していきます。
詳しいトランジスタの解説は参考サイトを紹介しておきます。
数値をトランジスタで表現する
では、具体的にトランジスタで 10進数を表現していきます。
ポイントは 0 と 1 を数字として見ないことです。ブーリアンの True / False と同じく、トランジスタの ON / OFF を表しているだけです。
ここでは 10 進数から 2 進数への変換の際の計算はコードで行っていきます。
詳しい変換方法は別記事でやっていきます。
2を表現する
Python で 2 を 8 ビットの2進数に変換するコードが下記です。
print(format(2, f'0{8}b'))
00000010
2を 2進数で表すと 00000010 になります。
この 0 と 1 はトランジスタに電流が流れているかどうかを表していて、
電流が流れている状態を 1 と表現します。

整数の 2 は 2 桁目のトランジスタのみが ON になっている状態です。
細かい話をすると、トランジスタはスイッチの働きをしているだけです。
トランジスタが ON の状態を 1 と表現しているのではなく
トランジスタを ON にすることで電流が流れている状態を 1 と表現しています。
C言語の int を表現する
C 言語の int のサイズは 4 バイトです。
#include <stdio.h>
int main() {
printf("int のサイズ : %zu\n", sizeof(int));
return 0;
}
int のサイズ : 4
C 言語では int を表現するために 4 バイトを使用します。
つまり、4×8 で 32 ビット、32個のトランジスタが必要になります。

4 バイトで 2 を表現する
1バイトだと 2 は 00000010 でした。3 バイト増えた場合は 0 で埋めていきます。
そのため、00000000000000000000000000000010 が 2 を表します。

4 バイトで 200万 を表現する
200万を 4 バイトの 2 進数で表現すると
00000000000111101000010010000000 になります。
Python だと下記のコードで確認できます。
print(format(2000000, f'0{32}b'))
00000000000111101000010010000000
同様に図にすると下記のようになります。

4 バイトで表現できる値の種類
トランジスタは ON か OFF の2値の表現しかできません。
32 bit = 32 個のトランジスタで表現できる値の個数は 2 の 32 乗で 4,294,967,296 、
約 43億種類の値を表現できます。

4バイトを超えた場合
4 バイトで表現できる値の種類は 4,294,967,296 種類ですが、これは 0 を含んでいません。
4,294,967,296 から 1 を引いた 4,294,967,295 が 2 進数で表現できる整数の最大値です。
そのため 0 ~ 4,294,967,295 が 4 バイトで表現できる整数の範囲になります。
C 言語は 0 に戻る
C言語で表現可能な範囲を超えた場合は 0 に戻ります。
下記は C言語で 4バイトを超えた場合の数値を出力するコードです。
#include <stdio.h>
int main() {
int num = 4294967295;
printf("ギリギリ 4 バイト -1 の値 : %u\n", num - 1);
printf(" ギリギリ 4 バイトの値 : %u\n", num);
printf(" 4バイトを 1 超えた値 : %u\n", num + 1);
printf(" 4バイトを 2 超えた値 : %u\n", num + 2);
printf(" 4バイトを 3 超えた値 : %u\n", num + 3);
return 0;
}
ギリギリ 4 バイト -1 の値 : 4294967294
ギリギリ 4 バイトの値 : 4294967295
4バイトを 1 超えた値 : 0
4バイトを 2 超えた値 : 1
4バイトを 3 超えた値 : 2
4,294,967,296 を表現するには 4 バイト + 1 ビットの数、33個のトランジスタが必要になります。
ですが C 言語で扱えるのは 4バイトのみのため、4 バイトを超えた値は 0 に戻ってしまいます。

1 と 4,294,967,297 は C 言語で扱える 4 バイト分のトランジスタの状態が全く同じになるため、両方とも 1 を表すことになります。
Python は限界を超えても計算できる
Python は整数の大きさに関して動的にメモリを割り当てています。
そのため C言語のようにビット数に縛られることなく、大きな数値も正確に扱うことができます。
def main():
num = 4294967295
print('ギリギリ 4 バイト -1 の値 : {}'.format(num - 1))
print(' ギリギリ 4 バイトの値 : {}'.format(num))
print(' 4バイトを 1 超えた値 : {}'.format(num + 1))
print(' 4バイトを 2 超えた値 : {}'.format(num + 2))
print(' 4バイトを 3 超えた値 : {}'.format(num + 3))
if __name__ == '__main__':
main()
ギリギリ 4 バイト -1 の値 : 4294967294
ギリギリ 4 バイトの値 : 4294967295
4バイトを 1 超えた値 : 4294967296
4バイトを 2 超えた値 : 4294967297
4バイトを 3 超えた値 : 4294967298
Python は整数の大きさに関して動的にメモリを割り当てています、と軽く書きましたが、正気私はよく理解できていません。
Python の整数は PyLongObject と呼ばれる構造体で表されています。
他にも文字列は PyUnicodeObject、浮動小数点数は PyFloatObject で表現されます。ここから先はややこしそうなんで今のところ手を出していません。
まとめ
2 進数で表現する理由はトランジスタを使って数値を表現しているためです。

電流が流れているかどうかの 2種類の値しか表現できないため、2 進数を扱っています。
C 言語の整数は 4 バイト以上を扱えませんが、Python であれば無制限の数の整数を扱えます。
負の値、小数の表現方法、演算なんかも今後書いていく予定です。
とりあえず次回は DRAM のメモリセルを見ていきます。

LEAVE A REPLY