Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2016年11月05日] [更新 2021年01月18日] Delphi 一般・その他
   
符号なし整数型と符号付き整数型
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro
[整理番号 - 0170]







符号なし整数型と符号付き整数型


整数型の変数の型には,符号なしと符号付きがあります.
コンピュータ内部,メモリに記録されているデータそのものには符号付き,符号なしの区別はありません.参考リンクの「内部データの形式」が示すように,変数の型によって,そのデータの使われ方が違います.したがって,データを使用する際,符号付きとして扱うか,符号なしで扱うか,何ビットの整数型の変数に代入するかによって結果が異なることがあります.






01_各種ビット数の整数型


数学の整数では無限小から, 0, 無限大までを扱います.しかし,コンピュータの内部では無限小や無限大という値は扱えません.そのため,ビット数を制限した整数型を使用します.

Delphi には以下のような整数型があります.IntXX は符号付きの整数型,UIntXX は符号なしの整数型です.先頭が U (Unsigned) の型が,符号なしの整数型です.XX の部分は,その整数型が使用するビット数です.
Byte は 1 バイト (8 ビット) の符号なし整数型です.その 2 倍の WORD は,2 バイト (16 ビット) の符号なし整数型です.よく使用されていいる DWORD は,その 2 倍,つまり,ダブルワードの 4 バイト (32 ビット) の符号なし整数型です.

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



  // Delphi の整数型と Windows API で使用する整数型
  // これは Windows の場合
  // 例 : LongWord は iOS および Linux では 64 ビット符号なし整数型

  Int8  = ShortInt   
  Int16 = SmallInt = SHORT  
  Int32 = Integer = LongInt 
  Int64 = LONG64 = LONGLONG

  UInt8  = Byte 
  UInt16 = WORD 
  UInt32 = Cardinal = DWORD = LongWord = UINT
  UInt64 = DWORDLONG = ULONGLONG  
  
  // 32 ビットの EXE では 32 ビットに,64 ビットの EXE では 64 ビットの整数型になる型
  NativeInt
  NativeUInt


NativeInt 型は符号付きの整数型です.NativeUInt 型は符号なしの整数型です.
ウィンドウハンドルの値に使用する THandle 型も NativeUInt 型です.





02_数値型の変数の最大値と最小値


整数型の数値データの範囲は,Low 関数で最小値,High 関数でその最大値を取得できます.それ以外にも各データ型専用の変数または定数が用意されていることがあります.例えば MaxInt は Integer 型の最大値です.これは High(Integer) と同じ値です.
整数型の最大値と最小値には同じビット数の整数型では,以下の関係があります.符号なし整数型の最小値は 0 です.


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

似たような名前の関数に LOWORD, HIWORD がありますが,これらは引数の DWORD の値から下位と上位の WORD 値を取得するための関数です.



実数型の最大値や最小値の値は,Math ユニットに Max..., Min... という名前で定義されています.
また,TDoubleHelper, TExtendedHelper にも定義がありますが,TDoubleHelper, TExtendedHelper 共に Delphi XE には実装されていません.



固定小数点形式の Currency 型は,64 ビットの符号付き整数型として定義されていますが,使用する時は実数として扱います.Low, High 関数では最小値,最大値は取得できません.
Currency 型の最小値と最大値は,次のコードのように,各々 MinCurrency, MaxCurrency で取得できます.CurrToStr 関数で,実際の実数表示での値を文字列として取得できます.Currency を扱う関数類は,SysUtils ユニットにあります.



リスト1
procedure TForm1.Button1Click(Sender: TObject);
begin
  Memo1.Lines.Add(CurrToStr(MinCurrency));
  Memo1.Lines.Add(CurrToStr(MaxCurrency));
end;


関数名・定数名等 ユニット 説明
Low(XXX) System 任意の整数型の最小値 (XXX は Integer, DWORD 等の型名)
High(XXX) System 任意の整数型の最大値 (XXX は Integer, DWORD 等の型名)
MaxInt System Integer (32 ビット符号付き整数) 型の最大値 = $7FFFFFFF
Delphi XE7 以前には MinInt という定義はない
MaxLongInt System LongInt (32 ビット符号付き整数) 型の最大値 = $7FFFFFFF
Delphi XE7 以前には MinLongInt という定義はない
MAXBYTE Windows BYTE (8 ビット符号なし整数) 型の最大値 = 255
MINSHORT Windows 符号付き 16 ビット整数型の最小値 = $8000
MAXSHORT Windows 符号付き 16 ビット整数型の最大値 = 32767
MINLONG Windows 符号付き 32 ヒット整数型の最小値 = DWORD($80000000)
MAXLONG Windows 符号付き 32 ビット整数型の最大値 = $7FFFFFFF
MAXWORD Windows 符号なし 16 ビット整数型の最大値 = 65535
MAXDWORD Windows 符号なし 32 ビット整数型の最大値 = DWORD($FFFFFFFF)
MinSingle Math Single 型 (16 ビット実数型) の最小値 = 1.401e-45
MaxSingle Math Single 型 (16 ビット実数型) の最大値 = 3.403e+38
MinDouble Math Double 型 (32 ビット実数型) の最小値 = 4.941e-324
MaxDouble Math Double 型 (32 ビット実数型) の最大値 = 1.798e+308
MinExtended Math Extended 型の最小値 = 1e-4932
64 ビットのアプリでは MinDouble と同じ
MaxExtended Math Extended 型の最大値 = 1.19e+4932
64 ビットのアプリでは MaxDouble と同じ




03_ビット数が異なる整数型の代入互換性


整数型の変数には代入互換性があります.ビット数が多い整数型の変数に代入する場合は,その変数が扱える範囲内であれば代入可能ですが,その逆の場合は注意が必要です.
符号にも注意が必要です.



図1
16 ビットの符号なし整数型の値を超える値を 16 ビットの符号なし整数型の変数に代入
  • 変数での代入は可能だが,結果は全く違う値になる
  • 16 ビットを超える数値の直接代入は不可

リスト2
procedure TForm1.Button1Click(Sender: TObject);
var
  LUInt16 : UInt16;
  LUInt32 : UInt32;
begin
  Memo1.Lines.Clear;

  // 代入先のビット数が大きければ,そのビット数の範囲内であれば代入可能
  LUInt16 := High(UInt16);
  LUInt32 := LUInt16 + 500;
  Memo1.Lines.Add(IntToStr(LUInt16));
  Memo1.Lines.Add(IntToStr(LUInt32));
//
  // UInt16 で扱える範囲を超えていても変数であれば代入可能
  // ただし値は正しくない
  LUInt32 := High(UInt16);
  LUInt16 := LUInt32 + 500;
  Memo1.Lines.Add(IntToStr(LUInt16));

  // これはコンパイル時にエラーとなる
  // UInt16 で扱える範囲を超えている
//  LUInt16 := 66035;
end;




04_符号なし整数型と符号付き整数型の代入互換性


整数型の変数には代入互換性がありますが,符号なし整数型と符号付き整数型とでは,同じビット数の整数型でも最小値と最大値が違います.符号なし整数型では負の値は正常に処理できません.これらのことに注意する必要があります.
16 ビットの整数型でそのことを確認してみます.

図の下のコードは,16 ビットの符号付き整数型の変数に,16 ビットの符号なし整数型の変数を代入する例です.単に変数に変数を代入しているだけです.数値を直接代入することもできます.



図2
符号付き整数型の変数に,符号なし整数型の正の値を代入
  • 元の数値と同じになっている

リスト3
//=============================================================================
//  符号付き整数型の変数に符号なし整数型の変数を代入
//  符号付き整数型の最大値以下の値を代入する場合
//  単に代入するだけでよい
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LInt16  : Int16;
  LUint16 : Uint16;
begin
  Memo1.Lines.Clear;

  LUInt16 := 32000;
  Lint16  := LUInt16;

  Memo1.Lines.Add('元の値-符号なし整数値 : ' + UIntToStr(LUint16));
  Memo1.Lines.Add('結果値-符号付き整数値 : ' + IntToStr(Lint16));
end;


上のサンプルと同じく,16 ビットの符号付き整数型の変数に,16 ビットの符号なし整数型の変数を代入数を代入する例です.代入する値が,符号付き整数型の最大値を超えている例です.
結果を見ると元の値とは違う値になっています.
16 ビットの符号付き整数型の変数に,数値 50000 を直接代入することはてきません.この値は,16 ビットの符号付き整数型の最大値を超えています.



図3
符号付き整数型の変数に,符号なし整数型の正の値を代入
  • 符号付き整数値型の最大値を超える値を代入
  • 絶対値が違う負の値になっている

リスト4
//=============================================================================
//  符号付き整数型の変数に符号なし整数型の変数を代入
//  符号付き整数型の最大値を超える値を代入する場合
//  代入は可能だが期待する値にならない
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LInt16  : Int16;
  LUint16 : Uint16;
begin
  Memo1.Lines.Clear;

  LUInt16 := 50000;
  Lint16  := LUInt16;

  Memo1.Lines.Add('元の値-符号なし整数値 : ' + UIntToStr(LUint16));
  Memo1.Lines.Add('結果値-符号付き整数値 : ' + IntToStr(Lint16));

  Memo1.Lines.Add('');
  Memo1.Lines.Add('符号付き整数値の最大値 : ' + IntToStr(High(Lint16)));
end;


今度は逆に,16 ビットの符号なし整数型の変数に,16 ビットの符号付き整数型の変数を代入する例です.負の値を代入しています.
結果を見ると元の値とは違う値になっています.符号なし整数型は負の値を負の値として処理しません.符号なし整数型は,代入された変数のバイナリデータを正の値として処理します.符号なし整数型の変数に,負の値を直接代入することはできません.



図4
符号なし整数型の変数に,符号付き整数型の変数を代入
  • 負の値を代入
  • 絶対値が違う正の値になっている

リスト5
//=============================================================================
//  符号なし整数型の変数に符号付き整数型の変数を代入
//  負の値を代入する場合
//  代入は可能だが期待する値にはならない
//
//  符号なし整数型の変数に負の値は直接代入できない
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LInt16  : Int16;
  LUint16 : Uint16;
begin
  Memo1.Lines.Clear;

  LInt16  := -12000;
  LUint16 := LInt16;

  Memo1.Lines.Add('元の値-符号付き整数値 : ' + IntToStr(Lint16));
  Memo1.Lines.Add('結果値-符号なし整数値 : ' + UIntToStr(LUint16));
end;




05_整数を文字列にする IntToStr 関数と UIntToStr 関数


整数値を文字列に変換する関数として IntToStr 関数と UIntToStr 関数があります.IntToStr 関数は符号付き整数型用です.UIntToStr 関数は符号なし整数型用です.
Format 関数も整数値を文字列に変換できます.
Format 関数で文字列にする場合,型の指定を d にすると,数値を符号付きの値として処理します.符号なしの数値として処理するには,型の指定を u にします.



下図は,32 / 64 ビットの整数値を IntToStr 関数と UIntToStr 関数で数文字にした結果です.符号付きと符号なしの正の整数値の例となっています.
符号付きの整数値では IntToStr 関数でも UIntToStr 関数でも同じ結果となりますが,64 ビットの符号なし整数値では正しい値になっていません.64 ビットの符号なし整数値でも,符号なし整数型の最大値以下の値であれば正しい結果となりますが,符号なし整数型の値の場合は,UIntToStr 関数を使用した方が無難です.



図5
IntToStr 関数と UIntToStr 関数の結果
  • 符号付きと符号なしの 32 / 64 ビットの整数型の最大値を表示

リスト6
//=============================================================================
//  IntToStr と UIntToStr 関数による処理結果
//  正の値の場合
//
//  符号付き整数型であれば 64 ビットまでどちらの関数でも処理可能
//  符号なし整数型の場合は,32 ビットまでならばどちらの関数でも処理可能
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LInt32  : Int32;
  LUInt32 : UInt32;
  LInt64  : Int64;
  LUInt64 : UInt64;
begin
  Memo1.Lines.Clear;

  // 符号付き 32 ビット整数型の場合
  LInt32 := High(Int32);
  Memo1.Lines.Add(IntToStr(LInt32));
  Memo1.Lines.Add(UIntToStr(LInt32));

  // 符号なし 32 ビット整数型の場合
  LUInt32 := High(UInt32);
  Memo1.Lines.Add(IntToStr(LUInt32));
  Memo1.Lines.Add(UIntToStr(LUInt32));

  // 符号付き 64 ビット整数型の場合
  LInt64 := High(Int64);
  Memo1.Lines.Add(IntToStr(LInt64));
  Memo1.Lines.Add(UIntToStr(LInt64));

  // 符号なし 64 ビット整数型の場合
  LUInt64 := High(UInt64);
  Memo1.Lines.Add(IntToStr(LUInt64));
  Memo1.Lines.Add(UIntToStr(LUInt64));
end;


負の整数値を IntToStr 関数と UIntToStr 関数を使用して文字列にした結果です.
UIntToStr 関数は,負の値を負の値として処理しません.引数のバイナリデータを正の整数値として処理して文字列に変換します.



図6
負の整数値を文字列に変換
  • UIntToStr 関数の結果は,絶対値が違う正の値になる

リスト7
//=============================================================================
//  符号付き 16 ビット整数値を文字列にして表示
//  IntToStr と UIntToStr 関数による結果の違い
//  UIntToStr 関数は負の値を認識しない
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LValue : Int16;
begin
  LValue := -15000;

  Memo1.Lines.Clear;
  Memo1.Lines.Add(IntToStr(LValue));
  Memo1.Lines.Add(UIntToStr(LValue));
end;