Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2021年01月06日] [更新 2021年01月19日] Delphi サンプルプログラム集
980_数値の 2 進数表示と 16 進数表示
動作確認等 Windows 7 U64(SP1) + Delphi XE5(UP2) Pro VCL-32
980_Binary_Hexadecimal.zip [13.0 MB] 2021年01月19日版 (EXE 同梱)







数値の 2 進数表示と 16 進数表示


かっては 8 進法を使用する種族もいたようですが,現在の人類は,数値を扱う時に 10 進法と呼ばれる 10 進数を使用します.単に数値と言った場合,それは 10 進数の数値を意味します.
現在普及しているコンピュータのデータは 2 進数の 0 と 1 のバイナリデータです.2 進数の 1 桁をビットといいます.バイナリデータを表示する場合,通常は 4 ビットを単位とした 16 進数を使用します.本ページは,バイナリデータとしての数値の 2 進数による表示と 16 進数による表示に関するサンプルです.






01_整数型の値を 16 進数の文字列に変換


整数型の値を 10 進数の文字列にする場合,符号付き整数型の時は IntToStr 関数を使用します.符号なし整数型の時は UIntToStr 関数を使用します.

整数型の値を 16 進数表記の文字列にするには,IntToHex 関数を使用します.IntToHex 関数は,符号なし整数型,符号付き整数型のどちらでも使用可能です.Format 関数も整数の値を 16 進数の文字列に変換できます.整数型のヘルパ,IntegerHelper 等の ToHexString メソッドも利用できます.
整数型の値を 16 進数の文字列にした時,その 16 進数は,その整数のコンピュータにおけるバイナリデータの値です.整数型の値が 0 と正の値の時 (0 以上の値の時) ,整数型の値のバイナリデータの 16 進数は,数学的な 16 進数と同じ値です.
16 進数の表記であることを明確にするには,Delphi では,プリフィックスとして $ 付けます.

実数型の値のバイナリデータを 16 進数で取得する関数は,Delphi には実装されていません.



図1
10 進数の 255 を 16 進数で表示
  • IntToHex 関数等で 16 進数の文字列にできる

リスト1
整数型の値を 16 進数の文字列に変換
//=============================================================================
//  整数型の値を 16 進数の文字列に変換
//
//  ToHexString メソッドの引数を省略すると必要な桁数だけの出力となる
//  IntToHex 関数の第 2 引数を 0 にすると必要な桁数だけの出力となる
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LValue : DWORD;
begin
  Memo1.Lines.Clear;

  LValue := 255;
  Memo1.Lines.Add(IntToStr(LValue));

  Memo1.Lines.Add('$' + Lvalue.ToHexString(4));
  Memo1.Lines.Add('$' + IntToHex(LValue, 4));
  Memo1.Lines.Add(Format('%s%4.4x', ['$', LValue]));
  Memo1.Lines.Add(Format('%s%x', ['$', LValue]));
end;


[備考 1]
バイナリデータは基本的にバイト単位の操作ですが,2 進数の文字列のサンプルのように,ビット操作で処理することもあります.

[備考 2]
文字のバイナリデータは,その文字が使用している文字符号化方式におけるコードポイント (その文字特有の値) です.Delphi 2009 以降では,UTF-16 という文字符号化方式が使われています.UTF-16 では,一般的に使用されている多くの文字のコードポイントは,16 進数で 4 桁の 2 バイトです.
コードポイントは,文字コードと言うこともあります.




02_整数型の最小値と最大値の 16 進数


整数型の最小値と最大値の 16 進数を確認してみます.
Windows API の関数では整数型の最大値を定数として使用することがあります.上のサンプルで使用した IntToHex 関数でその 16 進数を確認することができます.

4 バイト (32 ビット) の符号なし整数型の最小値は 0 です.最大値は $FFFFFFFF (16 進数 8 桁) です.先行する 0 は省略できますからこの表記ではバイト数が明確ではありません.$F と $0F は同じ値です. $FFFFFFFF は,符号付き 4 バイト整数型の -1 ですが,-1 を符号なし整数の値としては使用できません.そこで,以下のように,符号なし整数型でキャストする方法が使われることがあります.



図2
符号なし 4 バイト整数型の最大値は $FFFFFFFF
  • 符号なし 4 バイト整数型の最大値は,符号付き 4 バイト整数型では -1


procedure TForm1.Button1Click(Sender: TObject);
const
  CONST_VALUE = DWORD(-1);
var
  LInt32 : Int32;
begin
  Memo1.Lines.Clear;

  Memo1.Lines.Add(IntToStr(CONST_VALUE));

  Memo1.Lines.Add('$' + IntToHex(CONST_VALUE, 8));

  LInt32 := CONST_VALUE;
  Memo1.Lines.Add(IntToStr(LInt32));
end;


Delphi では THandle 型, NativeUInt 型も使用されています.これらは符号なし整数型の型です.以下の 3 つの定数はどれも,32 ビットの EXE では 16 進数 8 桁の $FFFFFFFF になり,64 ビットの EXE では 16 進数 16 桁の $FFFFFFFFFFFFFFFF となります.


const
  CONST_VALUE_1 = THandle(-1);
  CONST_VALUE_2 = NativeUInt(-1);
  CONST_VALUE_3 = High(NativeUInt);


Windows API の関数では符号付き整数型の (正の) 最大値も使用することもあります.4 バイト (32 ビット) の符号付き整数型の最大値は 16 進数 8 桁の $7FFFFFFF です.8 バイト (64 ビット) の符号付き整数型の最大値は 16 進数 16 桁の $7FFFFFFFFFFFFFFF です.これらの値は,次のように High 関数で取得することができます.Low 関数を使用すると整数型で扱う最小値を取得できます.


図3
符号付き 4 バイト整数型の最大値は $7FFFFFFF
  • 最小値 (負の最大値) は $80000000
  • 32 ビットの EXE での実行結果
図4
符号付き 8 バイト整数型の最大値は $7FFFFFFFFFFFFFFF
  • 最小値 (負の最大値) は $8000000000000000
  • 64 ビットの EXE での実行結果

procedure TForm1.Button1Click(Sender: TObject);
const
  // 符号付き整数型の最大値と最小値 (負の最大値)
  CONST_HIGH_VALUE = High(NativeInt);
  CONST_LOW_VALUE  = Low(NativeInt);
begin
  Memo1.Lines.Clear;

  Memo1.Lines.Add(IntToStr(CONST_HIGH_VALUE));
  Memo1.Lines.Add('$' + IntToHex(CONST_HIGH_VALUE, 8));

  Memo1.Lines.Add(IntToStr(CONST_LOW_VALUE));
  Memo1.Lines.Add('$' + IntToHex(CONST_LOW_VALUE, 8));
end;




03_16 進数表記を整数型の値として扱う


Delphi では,16 進数の先頭に $ (ドル・ドル記号) を追加すると整数型の値として扱えます.これは C / C++ の 0x と同じ機能です.
前項のサンプルで確認したように,4 バイトの符号付き整数型の最大値は $7FFFFFFF です.この値を超えるバイナリ値を 4 バイトの符号付き整数型の変数に代入すると負の値になります.下図はそのことを確認した結果です.
$D09DC300 は 4 バイトの符号なし整数型では 10 進数 10 桁の 3500000000 となります.4 バイトの符号付き整数型では -794967296 となります.



図5
16 進数の先頭に $ を付けると整数となる
  • 上 2 行は符号なし整数型の変数に 16 進数を代入
  • 下の 2 行は符号付き整数型の変数に同じ 16 進数を代入した結果
  • 4 バイトのバイナリデータの場合,$7FFFFFFF を超えると,4 バイトの符号付き整数型では負の値になる

リスト2
16 進数の先頭に $ を付けると整数型の値となる
//=============================================================================
//  16 進数表記を整数として扱う
//  16 進数の先頭に $ を追加すると整数型の値となる
//  $7FFFFFFF を超える値は符号付き整数型では負の値になる
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LUInt32 : UInt32;
  LInt32  : Int32;
begin
  Memo1.Lines.Clear;

  // 符号なし 32 ビットの整数型の変数に値を代入
  LUInt32 := $D09DC300;
  Memo1.Lines.Add('$' + IntToHex(LUInt32, 8));
  Memo1.Lines.Add(IntToStr(LUInt32));

  // 符号付き 32 ビット整数型の変数に上と同じ値を代入
  LInt32 := $D09DC300;
  Memo1.Lines.Add('');
  Memo1.Lines.Add('$' + IntToHex(LInt32, 8));
  Memo1.Lines.Add(IntToStr(LInt32));
end;


4 バイトの符号付き整数型の変数に $1BD を代入すると,445 になります.負の 16 進数の値 -$1BD を代入すると -445 になります.この -445 のバイナリデータを IntToHex 関数で 16 進数にすると,$FFFFFE43 になります.元の -$1BD にはなりません.$FFFFFE43 は,数学の 10 進数では 4294966851 になります.
-$1BD は,数学における 16 進数表記です.数学の 2 進数,16 進数には負の値がありますが,コンピュータのバイナリデータには負の値の概念はありません.



図6
16 進数 -$1BD を 4 バイトの符号付き整数型の変数に代入
  • 代入したのは数学的な負の 16 進数の値
  • 結果は負の値になる
  • バイナリデータは数学的な 16 進数の値にはならない
  • バイナリデータには負の値の概念はない
図7
$FFFFFE43 を 4 バイトの符号付き整数型の変数に代入
  • -455 になる
  • 16 進数の $FFFFFE43 は,数学では 4294966851
  • -445 は,数学の 16 進数では -$1BD

リスト3
$ の前に - (負号) を付けて,符号付き整数型の変数に代入すると負の値になる
//=============================================================================
//  先頭に - (負号) がある 16 進数を符号付き整数型に代入すると負の値になる
//
//  代入したのは数学の 16 進数の負の値
//  バイナリデータの 16 進数は数学における 16 進数の値とはならない
//  コンピュータのバイナリデータには負の値の概念はない
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LInt32 : Int32;
begin
  Memo1.Lines.Clear;

  // 16 進数を符号付き整数型の変数に代入
  LInt32 := $1BD;
  Memo1.Lines.Add('$1BD');
  Memo1.Lines.Add(IntToStr(LInt32));
  Memo1.Lines.Add('$' + IntToHex(LInt32, 8));

  // 先頭に - がある 16 進数を符号付き整数の変数に代入
  LInt32 := -$1BD;
  Memo1.Lines.Add('');
  Memo1.Lines.Add('-$1BD');
  Memo1.Lines.Add(IntToStr(LInt32));
  Memo1.Lines.Add('$' + IntToHex(LInt32, 8));
end;

//=============================================================================
//  上の結果の $FFFFFE43 を符号付き 4 バイト整数型の変数に代入
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LInt32 : Int32;
begin
  Memo1.Lines.Clear;

  // 16 進数を符号付き整数型の変数に代入
  LInt32 := $FFFFFE43;
  Memo1.Lines.Add('$FFFFFE43');
  Memo1.Lines.Add(IntToStr(LInt32));
  Memo1.Lines.Add('$' + IntToHex(LInt32, 8));
  Memo1.Lines.Add(UIntToStr(UInt32(LInt32)));
end;




04_$ 付きの 16 進数文字列は整数型の値に変換できる


10 進数の数字の文字列は StrToInt 関数または StrToInt64 関数で整数型の値に変換できます.同じように,先頭に $ を付加した 16 進数の文字列も整数型の値に変換できます.4 バイト (32 ビット) の整数型の値にする時は StrToInt関数を使用します.8 バイト (64 ビット) の整数型の値にする時は StrToInt64 関数を使用します.共に符号付き整数となりますが,整数型には代入互換性がありますから,符号なし整数型の変数に代入できます.

実数の数字の文字列は,StrToFloat 関数で実数型の値にできますが,16 進数の文字列は StrToFloat では実数型の値にできません.コンパイル時にエラーが発生します.



図8
$ 付きの 16 進数文字列は整数型の値に変換できる
  • 最初の値は,文字列 '$D09DC300' を符号なし 4 バイト整数型の値にした結果
  • 次の値は,結果の値のバイナリデータの値

リスト4
$ 付きの 16 進数文字列は,StrToInt 関数または StrToInt64 関数で整数型の値に変換できる
//=============================================================================
//  16 進数の文字列を整数型の値に変換
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LValue : DWORD;
begin
  Memo1.Lines.Clear;

  LValue := StrToInt('$' + Trim(Edit1.Text));
  Memo1.Lines.Add(IntToStr(LValue));
  Memo1.Lines.Add('$' + IntToHex(LValue, 1));
end;




05_2 進数の文字列を 16 進数の文字列に変換


Delphi には,2 進数を数値として扱う機能はありません.'0' と '1' だけの文字列を 2 進数の文字列と呼ぶことにします.この 2 進数の文字列であれば整数に変換できます.2 進数の文字列の各桁が,バイナリデータの各ビットに対応しているものとして処理します.

値が 0 の整数型の変数を用意します.Delphi で可能な最大の値まで扱えるように 64 ビットの整数型の変数とします.結果はこの整数に格納します.整数型は代入互換性がありますから,他のバイト数や符号なし整数型の値にもできます.
2 進数の文字が 1 の時は,該当ビットだけが 1 のバイナリ値をビット操作で作成して,結果の変数に加算していきます.結果の値を IntToHex 関数で 16 進数の文字列にすれば終了です.

このサンプルで扱えるのは,2 進数で 64 桁までの 64 ビット相当までです.64 ビットを超えるビット数の文字列を処理したい場合は,4 ビット単位で 16 進数の文字に変換する必要があります.



図9
2 進数の文字列を 16 進数の文字列に変換
  • 0, 1 以外の文字がある場合にも対応

リスト5
2 進数の文字列を 16 進数の文字列に変換
//=============================================================================
//  2 進数の文字列を 16 進数の文字列に変換
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
const
  LBIT64_1 : UInt64 = 1;
var
  LBitText   : string;
  LBitLen    : Integer;
  LInt64     : Int64;
  LShiftBits : Int64;
  LIndex     : Integer;
begin
  Memo1.Lines.Clear;

  LBitText := Trim(Edit1.Text);

  LShiftBits := 0;
  LInt64 := 0;
  LBitLen := Length(LBitText);
  for LIndex := 0 to LBitLen - 1 do begin
    if LBitText[LBitLen - LIndex] = '1' then begin
      LInt64 := LInt64 or (LBIT64_1 shl LShiftBits);
    end else begin
      // 0, 1 以外の文字の場合
      if LBitText[LBitLen - LIndex] <> '0' then Continue;
    end;
    LShiftBits := LShiftBits + 1;
  end;

  Memo1.Lines.Add(LBitText);
  Memo1.Lines.Add('$' + IntToHex(LInt64, 4));
end;




06_16 進数の文字列を 2 進数の文字列に変換


前項と逆の処理をするサンプルです.16 進数の文字列を 2 進数の文字列に変換します.

ビット演算で (LIndex + 1) ビット目が 1 であるかを調べます.1 の時は,2 進数の文字列に文字 1 を追加して,それ以外の時は 0 の文字を追加します.最下位のビットは LIndex = 0 の時です.このサンプルでは最下位ビットから処理しています.
16 進数の 1 桁である 4 ビットごとに区切り文字を挿入するようにしています.4 の剰余の計算には論理演算 and 3 を使用しています.これは mod 4 と同じです.

このサンプルで扱えるのは,16 進数で 16 桁までの 8 バイト相当までです.16 桁を超える 16 進数の文字列を処理したい場合は,16 進数の文字列を 1 文字単位で処理する必要があります.



図10
16 進数の文字列 2 進数の文字列に変換
  • 4 ビットごとに区切り文字を挿入

リスト6
16 進数の文字列)を 2 進数の文字列に変換
//=============================================================================
//  16 進数の文字列 2 進数の文字列に変換
//  4 ビット単位で区切り文字を挿入
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
const
  LBIT64_1 : UInt64 = 1;
var
  LValue   : Int64;
  LHexText : string;
  LBitLen  : Integer;
  LBitText : string;
  LAddText : string;
  LIndex   : Integer;
begin
  Memo1.Lines.Clear;

  // 16 進数の文字列を取得
  // ここで指定した (桁数 X 4) ビット分処理する
  LHexText := Trim(Edit1.Text);
  LBitLen  := Length(LHexText) * 4;

  LValue := StrToInt64('$' + LHexText);

  LBitText := '';
  LAddText := ' - ';
  for LIndex := 0 to LBitLen - 1 do begin
    if (LValue and (LBIT64_1 shl LIndex)) > 0 then begin
      LBitText := '1' + LBitText;
    end else begin
      LBitText := '0' + LBitText;
    end;

    // 4 ビットごとに空白文字等を挿入したい場合
    // 不要ならばこのコードを削除する
    if LAddText <> '' then begin
      if (((LIndex + 1) and 3) = 0) and ((LIndex + 1) < LBitLen) then begin
        LBitText := LAddText + LBitText;
      end;
    end;
  end;

  Memo1.Lines.Add('$' + LHexText);
  Memo1.Lines.Add(LBitText);
end;




07_符号付き整数型の負の値は 2 進数における補数


コンピュータのデータは 0 と 1 の バイナリです.バイナリデータには正の値と負の値の区別はありません.符号付き整数型を使用した処理で正と負の値を実現するために,コンピュータでは,整数型の値の 2 進数の補数を使用しています.

補数とは,整数 A, B を加算した時に桁上がりする数のことです.7 + 3 = 10 となり桁が増えます.この時 A は B の補数,B は A の補数と言います.2 進数では,1111 + 0001 = 10000 となり桁上がりします.この時 0001 は 1111 の補数です.

コンピュータの内部で行う計算は 2 進数による論理演算です.論理演算には負の値とか,減算 (引き算) という概念がありません.桁上がりした値から減算する方法は使用できまん.そこで,2 進数の補数を計算するには,元の 2 進数の全てのビットを反転して 1 を加算する方法が使用されています.
以下はその計算の確認用サンプルです

下図は,符号付き整数型の値 15 の 2 進数の補数を計算して,その補数の値を,同じ符号付き 4 バイトの整数型の値として取得して表示しています.元の 15 の負の値 -15 になっています.
15 と -15 の 2 進数を加算すると桁上がりしますから,計算した補数は,補数の定義に従った値であることになります.



図11
符号付き整数型の負の値
  • 最初の 2 進数は 15 の 2 進数
  • 次の 2 進数はその補数
  • その補数を 10 進数にすると -15

4 バイト (32 ビット) の符号付き整数型の値が 1 の場合です.10 進数の 1 を 2 進数にして補数にすると,10 進数では -1 です.-1 は 16 進数では $FFFFFFFF となります.

4 バイトの符号付き整数型の正の値の最大値は $7FFFFFFF です.絶対値が最大と言う意味での負の最大値は $80000000 です.4 バイトの符号付き整数型では,$FFFFFFFF から $80000000 の範囲を負の値として処理します.この範囲のバイナリデータの最上位ビットは 1 です.



図12
4 バイトの符号付き整数型の -1 のバイナリデータは $FFFFFFFF
  • 最初の 2 進数は符号付き整数型の 1 の 2 進数
  • 次の 2 進数はその補数
  • その補数を符号付き整数型の値にすると -1

リスト7
符号付き整数型の負の値は,2 進数では正の値の補数
  function Int32ToBitStr(AInt: Int32): string;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  符号付き整数型の符号変更は 2 進数における補数計算であることを確認するテストト
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LIntA    : Int32;
  LIntB    : Int32;
  LBitText : string;
begin
  Memo1.Lines.Clear;

  LIntA := StrToInt(Trim(Edit1.Text));

  // 正の値の場合
  Memo1.Lines.Add(IntToStr(LIntA) + ' ( $' + IntToHex(LIntA, 8) + ' )');
  LBitText := Int32ToBitStr(LIntA);
  Memo1.Lines.Add(LBitText);

  // 補数を作成
  // 全てのビットを反転して 1 を加算
  // それを別の符号付き整数型の変数に代入
  LIntB := not LIntA + UInt32(1);

  LBitText := Int32ToBitStr(LIntB);
  Memo1.Lines.Add(LBitText);
  Memo1.Lines.Add(IntToStr(LIntB) + ' ( $' + IntToHex(LIntB, 8) + ' )');
end;

//----------------------------------------------------------------------------
//  32 ピットの整数型の値を 2 進数の文字列に変換する関数
//----------------------------------------------------------------------------
function Int32ToBitStr(AInt: Int32): string;
const
  LBIT64_1 : UInt64 = 1;
var
  LDigit    : Integer;
  LBitText : string;
  LIndex   : Integer;
begin
  LDigit := SizeOf(AInt) * 8;

  LBitText := '';
  for LIndex := 0 to LDigit - 1 do begin
    if (AInt and (LBIT64_1 shl LIndex)) > 0 then begin
      LBitText := '1' + LBitText;
    end else begin
      LBitText := '0' + LBitText;
    end;

    // 4 ビットごとに空白文字等を挿入
    if (((LIndex + 1) and 3) = 0) and ((LIndex + 1) < LDigit) then begin
      LBitText := ' ' + LBitText;
    end;
  end;
  Result := LBitText;
end;


コンピュータの論理演算には減算 (引き算) の概念がありません.補数を使用して,負の値を加算するという方法で計算します.以下は符号付き 4 バイト整数型における 15 -7 = 8 の計算の例です.
15 を 2 進数にします.7 を 2 進数にしてその補数を計算します.この補数は,10 進数では -7 です.それらの 2 進数の値を加算 (足し算) します.加算すると桁上げが発生して 33 ビットになります.4 バイトの整数は 32 ビットですから ,33 ビット目は記憶できません.32 ビットだけが有効な桁数になります.結果は 2 進数で 1000 となります.これを 10 進数の値にすると 8 になります.これがコンピュータ内部で行われる演算です.

7 -15 = -8 のように,引かれる値の方が小さい値の時は,数値を入れ換えて減算処理をします.その結果の補数を計算結果とします.



図13
符号付き 4 バイト整数型における減算
  • 15 - 7 の計算例
  • 」最初の値は 15 の 2 進数
  • 次は 7 の 2 進数の補数
  • 次は 2 つの 2 進数の加算結果
  • 加算すると,33 ビット目がなくなるので 1000
  • 2 進数の 1000 は 10 進数の 8

リスト8
コンピュータ内部で行っている減算処理をエミュレート
//=============================================================================
//  整数型の減算 (引き算)
//  コンピュータ内部で行っている処理をエミュレート
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LUIntA   : UInt32;
  LUIntB   : UInt32;
  LIntC    : Int32;
  LBitText : string;
begin
  Memo1.Lines.Clear;

  LUIntA := 15;
  LUIntB := 7;

  LBitText := Int32ToBitStr(LUIntA);
  Memo1.Lines.Add(LBitText);

  // 補数を作成 (負の値にすることと同じ)
  LUIntB := not LUIntB + 1;
  LBitText := Int32ToBitStr(LUIntB);
  Memo1.Lines.Add(LBitText);

  // 加算する (減算と同じ結果になる)
  LIntC := LUIntA + LUIntB;
  LBitText := Int32ToBitStr(LIntC);
  Memo1.Lines.Add(LBitText);

  Memo1.Lines.Add(IntToStr(LIntC));
end;


[備考 1]
2 進数の 1111 1111 1111 1111 1111 1111 1111 0001 を -15 として扱えるのは,符号付き整数型の値として処理する時だけです.整数型のバイナリデータには,正の値と負の値の区別はありません.
2 進数は,2 進法で使用する数です.10 進数の -15 は,数学における 2 進法の 2 進数で表記すると -1111 です.数学では,先行する 0 は省略するのが一般的です.

コンピュータにおける計算は,論理演算を行う論理回路と呼ばれる電子回路を構成することによって実現しています.
論理演算でも 2 進数の 0, 1 を使用しますが,これは論理値です.数学における「数」ではありません.論理値には負の値の概念はありません.論理演算には減算という概念はありません.
論理回路や論理演算については,ネット上にも情報がありますが,おそらく,書籍の方が確実な知識が得られます.


[備考 2]
実数型のバイナリデータには符号用のビットがあります.




08_整数型の負の値と IntToHex 関数の出力桁数


上のサンプルで,コンピュータの内部の演算で減算を可能にするために,補数という概念が使用されていることを確認しました.この補数の仕様により,符号付き整数型の最大値と最小値の関係は以下のようになっています.


     符号付き整数型の最大値 = 符号なし整数型の最大値 div 2
     符号付き整数型の最小値 = -(符号なし整数型の最大値 div 2 + 1)

16 ビットの整数型の場合は以下のようになっています.
16 ビットで扱える最大値を 2 で割ると,最上位の 4 ビットは 0111 ($7) となるので,$7FFF が 16 ビットの符号付き整数型の最大値になります.負の最大値 (最小値) は,これに 1 を加算した $8000 になります.
他のビット数の整数型の場合も同じように計算できます.


//----------------------------------------------------------------------------
//  16 ビットの整数型の場合
//  65535 : 符号なし整数型の最大値
//  32767 : 符号付き整数型の最大値 = 65535 div 2
//
//  shr で右に 1 ビットシフトすると 2 による除算
//  shl で左に 1 ビットシフトすると 2 倍の値になる(最上位ビットが 0 の場合)
//----------------------------------------------------------------------------
   2 進数  1111 1111 1111 1111 shr 1 = 0111 1111 1111 1111
  16 進数                $FFFF shr 1 = $7FFF  // これを超える値 ($8000 以上) が負の値
  10 進数                65535 div 2 = 32767

コンピュータにおける 2 進数の補数を使用した演算では,2 つの整数型のビット数が同じでないと成立しません.そのため,コンピュータにおける演算では,短いビット数の整数型の値は,長いビット数の整数型のビット数に変換してから処理するようになっています.

下図は,正の整数値 15 を InttoHex 関数で 16 進数の文字列にした結果です.
IntToHex 関数の第 2 引数は,出力の 16 進数の桁数の指定です.この値を 0 にすると,有効な桁数だけの文字列となり,先行する 0 は出力しません.このサンプルでは 0 にしています.
下図は,8 / 16 / 32 / 64 ビットの符号付き整数型における結果ですが,全て $F になっています.



図14
整数 15 を IntToHex 関数で 16 進数表示
  • IntToHex 関数の第 2 引数を 0 にして,16 進数の桁数を自動設定
  • 上から順に 8 / 16 / 32 / 64 ビットの符号付き整数型の結果

リスト9
整数 15 を IntToHex 関数で 16 進数表示・16 進数の桁は自動設定
//=============================================================================
//  IntToHex 関数の第 2 引数が 0 で整数値が正の値の時
//  IntToHex の結果は先行する 0 を省略した結果となる
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LInt8  : Int8;
  LInt16 : Int16;
  LInt32 : Int32;
  LInt64 : Int64;
begin
  Memo1.Lines.Clear;

  // 1 バイト (8 ビット) の符号付き整数型
  LInt8 := 15;
  Memo1.Lines.Add(IntToHex(UInt8(LInt8), 0));

  // 2 バイト (16 ビット) の符号付き整数型
  LInt16 := 15;
  Memo1.Lines.Add(IntToHex(UInt16(LInt16), 0));

  // 4 バイト (32 ビット) の符号付き整数型
  LInt32 := 15;
  Memo1.Lines.Add(IntToHex(LInt32, 0));

  // 8 バイト (64 ビット) の符号付き整数型
  LInt64 := 15;
  Memo1.Lines.Add(IntToHex(LInt64, 0));
end;


下図は,負の整数値 -15 を InttoHex 関数で 16 進数の文字列にした結果です.
結果の最初の FFFFFFF1 は,8 ビットの符号付き整数型で,16 進数の桁数を 2 にした場合ですが,8 桁になっています.負の値の最上位ビットは常に 1 ですから,全てのビットが有効です.IntToHex 関数の第 1 引数の型は,32 ビットまたは 64 ビットの符号付き整数型です.最低でも 32 ビット,つまり 16 進数 8 桁の出力となります.
8 / 16 ビットの符号付き整数型の負の値を,指定の桁数の 16 進数で出力するには,IntToHex 関数の第 1 引数を UInt8 あるいは UInt16 でキャストします.



図15
負の整数 -15 を IntToHex 関数で 16 進数表示
  • 最初の結果は 8 ビットの符号付き整数型で,16 進数の桁数を 2 にした場合
  • 2 / 3 番目は,8 / 16 ビットの符号付き整数型
  • IntToHex 関数の引数を符号なし整数型でキャスト

リスト10
負の整数 -15 を 16 進数の桁数を指定して IntToHex 関数で 16 進数表示
//=============================================================================
//  同じ -15 でも,符号付き整数型のビット数によって 16 進数の値は違う
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LInt8  : Int8;
  LInt16 : Int16;
  LInt32 : Int32;
  LInt64 : Int64;
begin
  Memo1.Lines.Clear;

  // 1 バイト (8 ビット) の符号付き整数型
  LInt8 := -15;
  Memo1.Lines.Add(IntToHex(LInt8, SizeOf(LInt8) * 2));
  Memo1.Lines.Add('');

  // 1 バイト (8 ビット) の符号付き整数型
  // Byte 型 (1 バイト) の変数には負の値は代入できない
  // IntToHex の結果を 16 進数 2 桁で取得するには UInt8 でキャストする
  LInt8 := -15;
  Memo1.Lines.Add(IntToHex(UInt8(LInt8), SizeOf(LInt8) * 2));

  // 2 バイト (16 ビット) の符号付き整数型
  // IntToHex の結果を 16 進数 4 桁で取得するには UInt16 でキャストする
  LInt16 := -15;
  Memo1.Lines.Add(IntToHex(UInt16(LInt16), SizeOf(LInt16) * 2));

  // 4 バイト (32 ビット) の符号付き整数型
  LInt32 := -15;
  Memo1.Lines.Add(IntToHex(LInt32, SizeOf(LInt32) * 2));

  // 8 バイト (64 ビット) の符号付き整数型
  LInt64 := -15;
  Memo1.Lines.Add(IntToHex(LInt64, SizeOf(LInt64) * 2));
end;


[備考 2]
Byte 型は負の値が扱えませんが,同じ 8 ビットの Int8 型であれば負の値を扱えます.




09_実数型の値の 16 進数表示


コンピュータにおける整数型の 16 進数は,その整数のバイナリデータです.整数が 0 と正の値の時は,その 16 進数は,数学的な 16 進数と同じ値です.

コンピュータで扱う実数型の内部表現は,IEEE 754 仕様の浮動小数点数形式です.コンピュータにおける実数型の 16 進数表示は,IEEE 754 の浮動小数点数形式のバイナリデータの値を意味します. この 16 進数の値は,実数型の数学的な 16 進数の値ではありません.
16 進数の先頭に $ を追加して実数型の変数に代入しても,その 16 進数の値は,その実数型の値のバイナリデータとはなりません.その実数型の値の整数部の値になるだけです.



4 バイト (32 ビット) の実数のバイナリデータの操作例です.TSingleHelper の Bytes プロパティを使用すると,Single 型のバイナリデータをバイト単位で操作できます.
以下のサンプルで Button1 をクリックすると,Single 型の数値 0.5 のバイナリデータをバイト単位で取得して 16 進数で表示します.Button2 をクリックすると,その 16 進数の値をバイト単位で書き込んで Single 型の値として表示します.


図16
Button1 クリック
  • 4 バイトの実数 0.5 のバイナリデータを 16 進数で表示
図17
Button2 クリック
  • Button1 クリックで取得した 16 進数を,4 バイト実数のバイナリデータとして処理した結果

リスト11
TSingleHelper の Bytes プロパティを使用して,4 バイト実数型のバイナリデータを操作
var
  FHexText : string = '00000000';

//=============================================================================
//  Single 型の値を 16 進数の文字列に変換
//  TSingleHelper の Bytes プロパティからバイトデータを取得する
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LValue : Single;
  LIndex : Integer;
begin
  Memo1.Lines.Clear;

  LValue := StrToFloat(Trim(Edit1.Text));
  Memo1.Lines.Add((FormatFloat('0.000000E+00', LValue)));

  FHexText := '';
  // リトルエンディアン (バイトオーダが逆順) と仮定した処理
  for LIndex := SizeOf(LValue) - 1 downto 0 do begin
    FHexText := FHexText + IntToHex(LValue.Bytes[LIndex], 2);
  end;
  Memo1.Lines.Add('$' + FHexText);
end;

//=============================================================================
//  16 進数の文字列を Single 型の値に変換
//  TSingleHelper の Bytes プロパティにバイトデータを代入していく
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LValue   : Single;
  LByteTxt : string;
  LIndex   : Integer;
begin
  Memo1.Lines.Clear;

  // 必ず 8 文字のこと
  Memo1.Lines.Add('$' + FHexText);

  // リトルエンディアン (バイトオーダが逆順) と仮定した処理
  for LIndex := 0 to SizeOf(LValue) - 1 do begin
    LByteTxt := '$' + Copy(FHexText, LIndex * 2 + 1, 2);
    LValue.Bytes[SizeOf(LValue) - LIndex - 1] := StrToInt(LByteTxt);
  end;
  Memo1.Lines.Add((FormatFloat('0.000000E+00', LValue)));
end;


実数型の変数に値を代入した時,そのバイナリデータを整数型の変数で取り出せれば,IntToHex 関数で 16 進数の文字列にできます.このような場合,変数の型の指定の時に absolute 指令を使用すると便利です. absolute 指令を使用すると,メモリアドレスが同じの,別の型の変数を宣言することができます.その absolute 指令を使用した 4 バイト実数の操作例です.



図18
Button1 クリック
  • 4 バイトの実数 1.5 のバイナリデータを 16 進数で表示
図19
Button2 クリック
  • Button1 クリックで取得した 16 進数のバイナリデータを,4 バイト実数として取得した結果

リスト12
absolute 指令を使用して,4 バイト実数型のバイナリデータを 16 進数で表示
var
  FHexText : string = '0';

//=============================================================================
//  Single 型の値を 16 進数の文字列に変換
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LSingle : Single;
  // 変数 LSingle に代入した値が UInt32 型のデータとして取得できる
  LUint32 : UInt32 absolute LSingle;
begin
  Memo1.Lines.Clear;

  LSingle := StrToFloat(Trim(Edit1.Text));

  FHexText := IntToHex(LUint32, 8);

  Memo1.Lines.Add(FormatFloat('0.000000E+00', LSingle));
  Memo1.Lines.Add('$' + FHexText);
end;

//=============================================================================
//  16 進数の文字列を Single 型の値に変換
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LUint32 : UInt32;
  // 変数 LUint32 に代入した値が Single 型のデータとして取得できる
  LSingle : Single absolute LUint32;
begin
  Memo1.Lines.Clear;

  LUint32 := StrToInt('$' + FHexText);

  Memo1.Lines.Add('$' + FHexText);
  Memo1.Lines.Add(FormatFloat('0.000000E+00', LSingle));
end;


メモリストリームを使用する 4 バイト実数型の操作例です.
Single 型の値をメモリストリームに書き込みます.そのメモリストリームのデータを 4 バイトの整数型の値として読み込んで,IntToHex 関数で 16 進数の文字列にする単純な処理です.このサンプルの処理は,上の absolute 指令を使用するサンプルと機能的には同じと言えます.


図20
Button1 をクリック
  • 4 バイトの実数 0.5 のバイナリデータを 16 進数で表示

リスト13
メモリストリームを使用して,4 バイトの実数型のバイナリデータを 16 進数で表示
var
  FHexText : string = '0';

//=============================================================================
//  Single 型の値を 16 進数の文字列に変換
//  absolute 指令と同じ機能のコード
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LStream : TBytesStream;
  LValue  : Single;
  LUInt32 : UInt32;
begin
  Memo1.Lines.Clear;

  LStream := TBytesStream.Create;
  try
    LValue := StrToFloat(Trim(Edit1.Text));
    Memo1.Lines.Add((FormatFloat('0.000000E+00', LValue)));

    // メモリストリームに 32 ビットの実数値を書き込む
    LStream.WriteData(LValue);

    // 書き込んだデータを 32 ビットの整数型の変数に読み込む
    LStream.Position := 0;
    LStream.ReadData(LUInt32);

    FHexText := IntToHex(LUInt32, 1);
    Memo1.Lines.Add('$' + FHexText);
  finally
    FreeAndNil(LStream);
  end;
end;

//=============================================================================
//  16 進数の文字列を Single 型の実数値に変換
//  absolute 指令と同じ機能のコード
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LStream : TBytesStream;
  LValue  : Single;
  LUInt32 : UInt32;
begin
  Memo1.Lines.Clear;

  LStream := TBytesStream.Create;
  try
    LUInt32 := StrToInt('$' + FHexText);
    Memo1.Lines.Add('$' + IntToHex(LUInt32, 1));

    // メモリストリームに 32 ビットの整数値を書き込む
    LStream.WriteData(LUInt32);

    // 書き込んだデータを 32 ビット実数型の変数に読み込む
    LStream.Position := 0;
    LStream.ReadData(LValue);

    Memo1.Lines.Add((FormatFloat('0.000000E+00', LValue)));
  finally
    FreeAndNil(LStream);
  end;
end;


[備考 1]
IEEE は Institute of Electrical and Electronics Engineers (電気電子技術者協会) の略です.アイトリプルイーという呼び方が一般的です.
IEEE 754 は,ISO(国際標準化機構) / IEC(国際電気標準会議)/ IEEE 共同の規格 (ISO/IEC/IEEE 60559) としても制定されています.

[備考 2]
IBM のメインフレームでは,実数の内部表現に独自の方式を使用していましたが,現在は IEEE 754 を使用しています.




10_浮動小数点数の バイナリデータの2 進数表示と 16 進数表示


前項の処理を関数にしてみました.前項のサンプルは 4 バイト (32 ビット) の実数型でテストしていますが,8 バイト (64 ビット) の実数型用の関数と 2 進数の文字列関係の関数も追加しています.
それらの関数の動作確認用のサンプルです.


図21
実行結果例
  • 実数 2.0 のバイナリデータを 16 進数表示
  • その 16 進数を 2 進数に変換
  • その 2 進数を 16 進数に変換
  • その 16 進数を実数のバイナリデータとして値を取得

リスト14
浮動小数点数の バイナリデータの2 進数表示と 16 進数表示
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    Edit1: TEdit;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

  function FloatToHex(AValue: Single): string; overload;
  function FloatToHex(AValue: Double): string; overload;
  function HexToFloat(AHexStr: string): Extended;
  function HexToBitStr(AHexText: string; AddStr: string = ''): string;
  function BitStrToHex(ABitText: string): string;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Edit1.Text := '2.0';
end;

//=============================================================================
//  浮動小数点数の 2 進数文字列と 16 進数文字列
//  LValue の型を Double にすれば Double 型の処理となる
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LValue   : Single;
  LHexText : string;
  LBitText : string;
begin
  Memo1.Lines.Clear;

  LValue := StrToFloat(Trim(Edit1.Text));
  // 元の値を表示
  Memo1.Lines.Add((FormatFloat('0.000000E+00', LValue)));

  // 16 進数の文字列に変換して表示
  LHexText := FloatToHex(LValue);
  Memo1.Lines.Add('$' + LHexText);

  // 16 進数の文字列を 2 進数の文字列に変換して表示
  LBitText := HexToBitStr(LHexText, ' ');
  Memo1.Lines.Add(LBitText);

  // 2 進数の文字列を 16 進数の文字列に変換して表示
  LHexText := BitStrToHex(LBitText);
  Memo1.Lines.Add('$' + LHexText);

  // 16 進数の文字列を実数に変換して表示
  LValue := HexToFloat(LHexText);
  Memo1.Lines.Add((FormatFloat('0.000000E+00', LValue)));
end;

//----------------------------------------------------------------------------
//  Single 型の値を 16 進数の文字列に変換する関数
//----------------------------------------------------------------------------
function FloatToHex(AValue: Single): string;
var
  LUint32 : UInt32 absolute AValue;
begin
  Result := IntToHex(LUint32, 8);
end;

//----------------------------------------------------------------------------
//  Double 型の値を 16 進数の文字列に変換する関数
//----------------------------------------------------------------------------
function FloatToHex(AValue: Double): string;
var
  LUint64 : UInt64 absolute AValue;
begin
  Result := IntToHex(LUint64, 16);
end;

//----------------------------------------------------------------------------
//  16 進数の文字列を Double 型の値に変換する関数
//  実数は代入互換性があるので Single 型の変数で受け取れる
//----------------------------------------------------------------------------
function HexToFloat(AHexStr: string): Extended;
var
  LUint64 : UInt64;
  LDouble : Double absolute LUint64;
  LUint32 : UInt32;
  LSingle : Single absolute LUint32;
begin
  if Length(AHexStr) > 8 then begin
    LUint64 := StrToInt64('$' + AHexStr);
    Result := LDouble;
  end else begin
    LUint32 := StrToInt('$' + AHexStr);
    Result := LSingle;
  end;
end;

//----------------------------------------------------------------------------
//  16 進数の文字列を 2 進数の文字列に変換する関数
//----------------------------------------------------------------------------
function HexToBitStr(AHexText: string; AddStr: string = ''): string;
const
  LBIT64_1 : UInt64 = 1;
var
  LValue   : Int64;
  LBitLen  : Integer;
  LBitText : string;
  LIndex   : Integer;
begin
  // 出力する 2 進数の桁数
  LBitLen := Length(AHexText) * 4;
  LValue  := StrToInt64('$' + AHexText);

  LBitText := '';
  for LIndex := 0 to LBitLen - 1 do begin
    if (LValue and (LBIT64_1 shl LIndex)) > 0 then begin
      LBitText := '1' + LBitText;
    end else begin
      LBitText := '0' + LBitText;
    end;

    // 4 ビットごとに空白文字等を挿入したい場合
    if AddStr <> '' then begin
      if (((LIndex + 1) and 3) = 0) and ((LIndex + 1) < LBitLen) then begin
        LBitText := AddStr + LBitText;
      end;
    end;
  end;
  Result := LBitText;
end;

//----------------------------------------------------------------------------
//  2 進数の文字列を 16 進数の文字列に変換する関数
//----------------------------------------------------------------------------
function BitStrToHex(ABitText: string): string;
const
  LBIT64_1 : UInt64 = 1;
var
  LBitLen    : Integer;
  LInt64     : Int64;
  LShiftBits : Integer;
  LIndex     : Integer;
begin
  LShiftBits := 0;
  LInt64 := 0;
  ABitText := Trim(ABitText);
  LBitLen := Length(ABitText);
  for LIndex := 0 to LBitLen - 1 do begin
    if ABitText[LBitLen - LIndex] = '1' then begin
      LInt64 := LInt64 or (LBIT64_1 shl LShiftBits);
    end else begin
      // 0, 1 以外の文字の場合
      if ABitText[LBitLen - LIndex] <> '0' then Continue;
    end;
    LShiftBits := LShiftBits + 1
  end;
  // IntToHex は桁数が不足すると自動で桁を追加してくれるので任せる
  Result := IntToHex(LInt64, 1);
end;

end.




11_実数の小数部の数学的な 2 進数と 16 進数


コンピュータの内部表現のバイナリデータではなく,実数を数学的な 2 進数と 16 進数に変換する例です.実数の小数部の数学的な 2 進数と 16 進数の値は,例えば以下のコードで計算できます.このサンプルでは,2 進数の文字列を 16 進数の文字列に変換する関数は,最下位ビットから処理しているため,2 進数は,4 の倍数ビット必要です.
実数の整数部は,IntToHex 関数で 16 進数の文字列に変換できます.

コンピュータで処理可能な桁数は有限ですから,このコードで処理可能な桁数も制限があります.
実数の 0.2 は,2 進数では無限小数となっています.コンピュータの内部演算は 2 進数です.したかって,コンピュータでは 0.2 という実数は正確に表現できまぜん. そのため,コンピュータによる数値計算では,誤差が伴うのが一般的です.



図22
0.2 の 2 進数と 16 進数の値
  • 無限小数になる
図23
0.5 の 2 進数と 16 進数の値

リスト15
実数の小数部の数学的な 2 進数と 16 進数
  function BitStrToHex(ABitText: string): string;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Edit1.Text := '0.2';
end;

//=============================================================================
//  実数の小数部だけの値を 2 進数と 16 進数に変換する処理例
//
//  数値を 2 倍した時の整数部の値 (常に 0 か 1) がビットの値
//  2 倍した値から整数部の値を除いて 2 倍にする
//  その値の整数部が次のビットの値
//  これを繰り返す
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
const
  LBITCOUNT_MAX = 32;
var
  LValue     : Extended;
  LNextValue : Extended;
  LOneBit    : Integer;
  LBitText   : string;
  LIndex     : Integer;
begin
  Memo1.Lines.Clear;

  LValue := StrToFloat(Trim(Edit1.Text));
  if Trunc(LValue) > 0 then Exit;

  Memo1.Lines.Add(FormatFloat('0.0000E+00', LValue));

  LNextValue := LValue;

  LBitText := '';
  LOneBit  := 0;
  for LIndex := 1 to LBITCOUNT_MAX do begin
    LNextValue := (LNextValue - LOneBit) * 2;
    LOneBit := Trunc(LNextValue);
    LBitText := LBitText + IntToStr(LOneBit);
    if (LIndex mod 4) = 0 then begin
      LBitText := LBitText + ' ';
    end;
  end;

  Memo1.Lines.Add('0.' + LBitText);
  Memo1.Lines.Add('$0.' + BitStrToHex(LBitText));
end;

//----------------------------------------------------------------------------
//  2 進数の文字列を 16 進数の文字列に変換する関数
//----------------------------------------------------------------------------
function BitStrToHex(ABitText: string): string;
const
  LBIT64_1 : UInt64 = 1;
var
  LBitLen    : Integer;
  LInt64     : Int64;
  LShiftBits : Integer;
  LIndex     : Integer;
begin
  LShiftBits := 0;
  LInt64 := 0;
  ABitText := Trim(ABitText);
  LBitLen := Length(ABitText);
  for LIndex := 0 to LBitLen - 1 do begin
    if ABitText[LBitLen - LIndex] = '1' then begin
      LInt64 := LInt64 or (LBIT64_1 shl LShiftBits);
    end else begin
      // 0, 1 以外の文字の場合
      if ABitText[LBitLen - LIndex] <> '0' then Continue;
    end;
    LShiftBits := LShiftBits + 1
  end;
  // IntToHex は桁数が不足すると自動で桁を追加してくれるので任せる
  Result := IntToHex(LInt64, 1);
end;




12_BCD 値のバイナリデータの 16 進数表示


Delphi の TBcd 型の変数 (BCD 値) のバイナリデータの 16 進数表示の例です.
Delphi の BCD 値は 34 バイト使用します.先頭の 2 バイトは数値の桁数関係の値です.残りの 32 バイトが小数点を除いた実際の数値です.10 進数の 1 桁に 16 進数 1 桁を使用しますから,10 進数で 64 桁まで扱えます.64 ビットの整数型では 19 桁程度ですから,かなり多い桁数と言えます.
BCD では,演算が 10 進数で行われるため,丸め誤差を抑えることができます.



図24
BCD 値のバイナリデータの例
  • 長い桁数の例
  • 64 桁まで格納できる
  • 先頭の 1 バイトは小数点を除いた桁数
    $24 = 16 X 2 + 4 = 36
  • 2 バイト目の先頭ビットが 1 の時は負の値
図25
BCD 値のバイナリデータの例
  • 小さい値の例
  • - (マイナス) 何乗まで使用可能かは有効桁数による

リスト16
BCD 値のバイナリデータの 16 進数表示
  function BCDToHex(ABcdValue: TBcd): string;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Memo1.Font.Charset := ANSI_CHARSET;
  Memo1.Font.Name    := 'Courier New';
  Memo1.Font.Size    := 10;

  Edit1.Font.Name := 'Courier New';
  Edit1.Font.Size := 10;
  Edit1.Text := '123456789012345678901234567890.123456';
end;

//=============================================================================
//  BCD 値のバイナリデータの 16 進数表示
//  uses に Data.FMTBcd が必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LValue   : TBcd;
  LHexText : string;
begin
  Memo1.Lines.Clear;

  // TBcd 型の変数に値を代入して表示
  LValue   := StrToBcd(Trim(Edit1.Text));
  Memo1.Lines.Add(FormatBcd('0.00000000', LValue));
  Memo1.Lines.Add(BcdToStrF(LValue, TFloatFormat.ffExponent, 7, 6));

  // TBcd 型の変数のバイナリデータを 16 進数の文字列に変換して表示
  LHexText := BCDToHex(LValue);
  Memo1.Lines.Add('');
  Memo1.Lines.Add(LHexText);
end;

//----------------------------------------------------------------------------
//  TBcd 型の変数のバイナリ値を 16 進数の文字列に変換する関数
//  uses に Data.FMTBcd が必要
//----------------------------------------------------------------------------
function BCDToHex(ABcdValue: TBcd): string;
var
  LStream   : TBytesStream;
  LStrList  : TStringList;
  LLineText : string;
  LIndex    : Integer;
begin
  Result := '';

  LStream  := TBytesStream.Create;
  LStrList := TStringList.Create;
  try
    // データをメモリストリームに書き込む
    LStream.Write(ABcdValue, SizeOf(ABcdValue));

    // 書き込んだデータをバイト単位で 16 進数の文字に変換していく
    LLineText := '';
    for LIndex := 0 to LStream.Size - 1 do begin
      if LIndex > 0 then begin
        if (LIndex mod 16) = 0 then begin
          LStrList.Add(TrimRight(LLineText));
          LLineText := '';
        end else
        if (LIndex mod 8) = 0 then begin
          LLineText := LLineText + ' ';
        end;
      end;
      LLineText := LLineText + IntToHex(LStream.Bytes[LIndex], 2) + ' ';

      Application.ProcessMessages;
    end;
    if LLineText <> '' then LStrList.Add(TrimRight(LLineText));

    Result := LStrList.Text;
  finally
    FreeAndNil(LStrList);
    FreeAndNil(LStream);
  end;
end;


[備考 1]
長い桁の数値は,数値としてコード内で直接代入できません.StrToBcd 関数を使用して,文字列から変換して変数に代入します.

[備考 2]
世界初の商用マイクロプロセッサである 4 ビットの CPU, Intel 4004 は電卓用として開発されました (1971年11月発売).開発の中心人物が日本人の嶋正利氏であったことでも知られています.この Intel 4004 の演算は BCD でした.当初は,2 進数による演算も考えられていたようですが,2 進数を 10 進数に変換する IC チップのコストの関係で見送られたという話があります.
Intel 4004 は 2,300 個のトランジスタで構成されていました.パソコンと呼ばれる現在のコンピュータの CPU は,その構成素子としてのトランジスタは 1 億個以上です.
本ページの記事掲載年が 2021年ですから,Intel 4004 誕生 50 周年です.