導入
C言語 - バイナリ/シフト演算子/ビット演算子_学習ノートの記事で説明したように、データはメモリ内にバイナリ形式、つまり 0 と 1 で格納されます。
而整数的二进制表示方法有三种,原码、反码和补码,文中也有所提及 而关于浮点数,浮点数在内存中也是存的是二进制,但是相关规则和整数的存储有很大不同
ここでは、整数と浮動小数点数がどのようにメモリに格納されるかを詳しく紹介します。
メモリ内の整数の保存
整数のバイナリ表現には、元コード、補コード、補コードの 3 つの方法があります。
3 つの表現方法にはすべて、符号ビットと数値ビットがあります。符号ビットは、「正」を表すために 0 を使用し、「負」を表すために 1 を使用します。数値ビットの最上位ビットは符号ビットとして使用され、残りは数値ビットです。
正の整数の元のコード、逆コード、および補数コードは同じです。
負の整数をさまざまな方法で表すには 3 つの方法があります。
元のコード: 元のコードは、数値を正負の形式のバイナリに直接変換することによって得られます。
1 の補数コード: 元のコードの符号ビットを変更せず、他のビットをビットごとに反転して 1 の補数コードを取得します。
補数コード: 補数コード + 1 が補数コードです。
整数の場合: メモリに格納されているデータには、実際には補数コードが格納されています。
理由:
コンピュータ システムでは、値は常に 2 の補数で表現され、格納されます。その理由は、補数コードを使用すると、符号ビットと数値フィールドを均一に処理できると同時に、加算と減算も均一に処理できるためです(CPU には加算器しかありません)。元のコードは相互に変換され、動作プロセスは同じですが、追加のハードウェア回路が必要です。
例(C言語、VS2020環境ではint型とバイナリ関連の表現は以下の通り)
整数の場合: メモリに格納されているデータには、実際には補数コードが格納されています。
これは本当ですか? 次のコードを実行してコンパイラ (VS2022、X64 環境) で検証し、デバッグウィンドウ
を開いて、
このコンパイラ環境でメモリ ウィンドウのデータが 16 進数でメモリに格納されていることを確認してみましょう。
上記の6の補数を16進数に変換すると、
実行結果とはまったく逆の結果になっているように見えますが、これはなぜでしょうか?
これにはエンディアンに関する知識が含まれます。
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ↓↓↓↓↓↓↓↓↓ ↓~~~~~~~~~~~~~~~~~~~~~
エンディアンネス
エンディアンとは何ですか
実際には、1 バイト以上のデータをメモリに格納する場合、格納順序の問題が発生します。格納順序の違いにより、ビッグエンディアン ストレージとリトル エンディアン ストレージに分けられます。具体的な概念は次のとおりです。
- ビッグエンディアン (ストレージ) モード: データの下位バイトの内容がメモリの上位アドレスに格納され、データの上位バイトの内容がメモリの下位アドレスに格納されることを意味します。
- リトル エンディアン (ストレージ) モード: データの下位バイトの内容がメモリの低位アドレスに格納され、データの上位バイトの内容がメモリの上位アドレスに格納されることを意味します。
なぜ大きな端と小さな端があるのですか?
これは、コンピュータ システムではバイトを単位としており、アドレスの単位が 1 バイトに相当し、1 バイトは 8 ビットであるためですが、C 言語では 8 ビットの文字のほかに 16 個の文字が存在します。 -bit short 型と 32-bit long 型 (特定のコンパイラによる) また、16 ビットまたは 32 ビットプロセッサなどの 8 ビットを超えるプロセッサの場合、レジスタ幅が 1 バイトより大きいため、となると、複数のバイトをどう配置するかという問題があるはずです。これは、ビッグエンディアン ストレージ モードとリトル エンディアン ストレージ モードにつながります。
例えば:
- 16 ビットの short 型 X、メモリ内のアドレスは 0x0010、および値は
- ビッグエンディアン モードの場合、下位アドレス (0x0010) に 0x11 を、上位アドレス (0x0011) に 0x22 を入力します。
- リトルエンディアンモードはその逆です。
一般的に使用される X86 構造はリトル エンディアン モードです、一方、KEIL C51 はビッグエンディアン モードです。多くの ARM および DSP はリトルエンディアン モードです。一部の ARM プロセッサでは、ハードウェアによってビッグ エンディアン モードまたはリトル エンディアン モードを選択することもできます。
現在のマシンのエンディアンを確認する方法
判断するための小さなプログラムを設計できます
int check_sys()
{
int a = 1;
return (*(char*)&a);//小端返回1,大端返回0
}
int main()
{
if (check_sys() == 1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}
まず整数変数 a=1 を定義します。a は 00 00 00 01 としてメモリに 16 進形式で格納されていることがわかっています。したがって、a のアドレスを取得し、a の開始アドレスから開始して、内容を取り出します。 1バイトの判定が可能です。リトルエンディアンの場合、結果は 1 になり、ビッグエンディアンの場合、結果は 0 になります。
浮動小数点データをメモリに保存する
浮動小数点数ファミリーには、float、double、long double 型が含まれます。
- float、double、long double 型は通常、それぞれ 4 バイト、8 バイト、16 バイトのメモリ空間を占有します。ただし、この値は固定されておらず、オペレーティング システム、コンパイラ、ハードウェア アーキテクチャによって異なる場合があります。
浮動小数点データの保管規則
国際標準 IEEE (電気電子工学研究所) 754 によると、任意の 2 進浮動小数点数 V は次の形式で表現できます。
IEEE754規定:
32 ビット浮動小数点数 の場合、最上位の 1 ビットは符号ビット S を格納し、次の 8 ビットは指数 E を格納し、残りの 23 ビットは有効数字 M を格納します。
64 ビット浮動小数点数 の場合、最上位の 1 ビットは符号ビット S を格納し、次の 11 ビットは指数 E を格納し、残りの 52 ビットは有効数字 M を格納します。
IEEE 754 には、有効数字 M と指数 E に関する特別な規定もいくつかあります。
- 前述したように、1<M<2、つまり M は 1.xxxxxx の形式で記述できます。xxxxxx は小数部を表します。
- IEEE754 では、M をコンピュータ内に保存する場合、デフォルトではこの数値の最初の 1 桁が常に 1 であるため、これを破棄して次の xxxxxx 部分のみを保存することが規定されています。たとえば、1.01を保存する場合は01のみが保存され、読み込む場合は最初の1が加算されます。これの目的は、有効数字 1 桁を節約することです。32 ビット浮動小数点数を例にとると、M には 23 ビットだけが残ります。最初の 1 が四捨五入されると、有効数字 24 桁を保存できます。
インデックス E に関しては、状況はさらに複雑です。
まず、E は符号なし整数 (unsigned int) です。
- これは、E が 8 ビットの場合、値の範囲は 0 ~ 255、E が 11 ビットの場合、値の範囲は 0 ~ 2047 であることを意味します。
- ただし、科学表記法の E は負の値になる可能性があることはわかっています。
- したがって、IEEE 754 では、E の実数値をメモリに格納するときに中間数に加算する必要があると規定しています。8 ビット E の場合、この中間数は 127 であり、11 ビット E の場合、この中間数は 1023 です。
- たとえば、2^10 の E は 10 であるため、32 ビット浮動小数点数として保存する場合は、10+127=137、つまり 10001001 として保存する必要があります。
例えば
(以下のように、値が5.0のfloat型変数fを定義します)
- 浮動小数点数 f = 5.0
- 浮動小数点 f = -5.0
10 進数の 5.0 は 2 進数では 101.0 であり、1.01×2^2 に相当します。次に、上記のVの形式に従って、S=0、M=1.01、E=2 を取得できます。
10 進数の -5.0 は、2 進数で表記すると -101.0 となり、-1.01×2^2 に相当します。すると、S=1、M=1.01、E=2となります。
-5 は最初のビットを 1 に変更するだけなので、詳細は説明しません。
浮動小数点数を取得するプロセス
メモリからのインデックス E の取得は、さらに 3 つの状況に分類できます。
- E はすべて 0 または不完全ではありません
このとき、浮動小数点数は、指数 E の計算値から 127 (または 1023) を引いて実数値を求め、有効数字 M の前に最初の 1 を加算するという規則で表現されます。
例: 0.5 の 2 進形式は 0.1 です。正の部分は 1、つまり小数点を 1 桁右に移動する必要があるため、1.0*2^(-1) となり、その指数コードは次のようになります。 -1+127 (中間値) = 126 、 01111110 として表され、仮数 1.0 は整数部分を削除して 0 にし、0 ~ 23 桁を
1 に埋めます。
- Eはすべて0です
このとき、浮動小数点数の指数 E は実数値である 1 ~ 127 (または 1 ~ 1023) となり、有効数 M は最初の 1 を加算するのではなく、10 進数の 0 に減算されます。 .xXXxxx。これは、±0、および 0 に近い非常に小さな数値を表すために行われます。
- Eはすべて1です
このとき、有効数字 M がすべて 0 であれば、±無限大(符号ビット s により正負が異なる)を意味します。
練習用アプリケーションの例
int main()
{
int n = 9;
float* pFloat = (float*)&n;
printf("n的值为:%d\n", n);
printf("*pFloat的值为:%f\n", *pFloat);
*pFloat = 9.0;
printf("num的值为:%d\n", n);
printf("*pFloat的值为:%f\n", *pFloat);//9.0
return 0;
}
実行結果:
説明は次のとおりです。