Delphi Programming / Object Pascal
ロゴマーク
[掲載 2012年07月13日] [更新 2021年01月15日] Delphi サンプルプログラム集
081_ビットフラグの検出と 16 進数表示
動作確認等 Windows 7 U64(SP1) + Delphi XE Pro
081_RadixConversion.zip [2.1 MB] 2021年01月15日版 (EXE 同梱)







ビットフラグの検出


指定のビット,すなわち,2 進数の指定桁の値が 1 であるかを検査するサンプルです.


2021年01月06日に以下の記事を追加しました.それに伴い,本ページに掲載していたサンプルの一部を削除しました.





01_指定ビットが立っているかを検査


与えられた数値の指定ビットが立っているか,つまり,指定ビットが 1 であるかを検査するテストプログラムです.
この検査にはビット演算子を使用しています.例えば shr というビット演算子を使用した

  A shr 3

という演算は,A という値を右に 3 ビットだけシフトします.A が 11101 であれば,

  A shr 3 = 00011 (11101 → 01110 → 00111 → 00011)

となります.右にシフトすると,最上位のビットは 0 になります.逆に shl で左にシフトすると最下位のビットは 0 になります.したがって,最下位から 5 ビット目が 1 であるかを調べるには,1 という数値を左に 4 ビットシフトして,5 ビット目だけが 1 の値を作成して,それと元の値との論理積が 1 であるかで判定することができます.論理積とは AND 演算のことです.


図1
設計時画面
図2
実行例

リスト1
数値の指定ビットが立っているか( 1 であるか)を検査
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    LabeledEdit1: TLabeledEdit;
    LabeledEdit2: TLabeledEdit;
    Button1: TButton;
    Button2: TButton;
    Panel1: TPanel;
    Panel2: TPanel;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

//=============================================================================
//  検査に使用するテスト用の数値をセット
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  LabeledEdit1.Text := '128';
  LabeledEdit2.Text := '8';
end;

//=============================================================================
//  検査対象の数値の,指定ビットが立っているか(1であるか)を検査
//  数値を右へビットシフトして調査
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  ValueTarget : Cardinal;
  ValueBit    : Cardinal;
  BitFlag     : Boolean;
  S           : String;
begin
  Panel1.Caption := '';
  Panel2.Caption := '';

  ValueTarget := StrToInt(LabeledEdit1.Text);
  ValueBit    := StrToInt(LabeledEdit2.Text);

  if ValueBit > 0 then begin
    //検査対象のValueTargetの値のValueBit目が1ならTrue
    BitFlag := (ValueTarget shr (ValueBit - 1)) = 1;

    S := '数値 ' + LabeledEdit1.Text + ' の ' + LabeledEdit2.Text + ' ビット目は ';
    if BitFlag then begin
      Panel1.Caption := S + '1 です';
    end else begin
      Panel1.Caption := S + '0 です';
    end;

    S := LabeledEdit2.Text + ' ビット目だけが 1 の 16 進数表記は ';
    Panel2.Caption := S + Format('$%.4x', [1 shl (ValueBit - 1)]);
  end;
end;

//=============================================================================
//  検査対象の数値の,指定ビットが立っているか(1であるか)を検査
//  数値を左へビットシフトして調査
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  ValueTarget : Cardinal;
  ValueBit    : Cardinal;
  BitFlag     : Boolean;
  S           : String;
begin
  Panel1.Caption := '';
  Panel2.Caption := '';

  ValueTarget := StrToInt(LabeledEdit1.Text);
  ValueBit    := StrToInt(LabeledEdit2.Text);

  if ValueBit > 0 then begin
    //検査対象のValueTargetの値のValueBit目が1ならTrue
    BitFlag := ((1 shl (ValueBit - 1)) and ValueTarget) <> 0;

    S := '数値 ' + LabeledEdit1.Text + ' の ' + LabeledEdit2.Text + ' ビット目は ';
    if BitFlag then begin
      Panel1.Caption := S + '1 です';
    end else begin
      Panel1.Caption := S + '0 です';
    end;

    S := LabeledEdit2.Text + ' ビット目だけが 1 の 16 進数表記は ';
    Panel2.Caption := S + Format('$%.4x', [1 shl (ValueBit - 1)]);
  end;
end;

end.


2 進数は,4 ビット単位で比較的簡単に 16 進数に変換できます.16 進数を使用すれば,上のサンプルは以下のように処理可能です.

  // 最下位ビットから 8 ビット目が 1 であるかを検査
  //  8 ビット目が 1 である 2 進数の 1000 0000 は 16 進数では $80

  if (Value and $80) > 0 then begin

  if (Value and $80) = $80 then begin


指定ビットだけが 1 であるかを調べるには,指定ビットが 1 だけの 16 進数と比較するだけです.

  if Value = $80 then begin
    // 
  end;


複数のビットの場合も同じようにして調べることができます.
以下は,16 進数表記を使用して,指定した複数のビットだけが 1 であるかを調べる例です.


//=============================================================================
//  指定した複数のビットだけが全て 1 であるかを検査
//  or 演算を使用する例
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  Value : UInt32;
begin
  Value := 193;

  // $C1 は 2 進数で 1100 0001
  if (Value or $C1) = $C1 then begin
    // 指定した複数のビットだけが全て 1 である場合
  end;
end;

//=============================================================================
//  指定した複数のビットだけが全て 1 であるかを検査
//  数値のバイナリデータを比較する
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  Value : UInt32;
begin
  Value := 193;

  // $C1 は 2 進数で 1100 0001
  if Value = $C1 then begin
    // 指定した複数のビットだけが全て 1 である場合
  end;
end;




02_コントロールキーの押下を検出


Windows API を使用する際,指定ビットが立っているか検査することがあります.
この場合,上のサンプルのように,ビットシフトの演算のコードを直接書いてもいいのですが,多くの場合,16 進数で論理積 (AND 条件)でコーディングするのが普通です.

次のサンプルは,ボタンをクリックした時に,[Shift] キー等を同時に押した状態でクリックしたかを検出するテストです.
GetKeyboardState 関数を使用していますが,この関数で取得した値の最上位ビットが 1 の場合,キーが押されていることを示しています.この引数の型は Byte 型ですから,8 ビットです.したがって,上のプログラムの結果から,8 ビット目だけが 1,つまり 2 進数の 10000000 というのは,16進数で 80 となります.そこで,この値との論理積で,キーの状態を調べることになります.


図3
設計時画面
図4
実行例
  • [Shift] キーと [Ctlr] キーを押したままボタンをクリックした場合

リスト2
コントロールキーの押下を検出
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Panel1: TPanel;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

//=============================================================================
//  このボタンをクリックした時に,各種キーが押されいたかを検査
//
//  KeyStateのメンバーの最上位ビットが1のときはキーが押されていることを示す
//  KeyStateの値はByte型なので,最上位ビットは8ビット目,つまり$80となる
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  KeyState : TKeyboardState;
  S        : String;
begin
  S := '';

  if GetKeyboardState(KeyState) then begin
    if KeyState[Ord('A')] and $80 <> 0 then S := S + ' [ A ] ';

    if KeyState[VK_MENU]    and $80 <> 0 then S := S + ' [ Alt ] ';
    if KeyState[VK_SHIFT]   and $80 <> 0 then S := S + ' [ Shift ] ';
    if KeyState[VK_CONTROL] and $80 <> 0 then S := S + ' [ Ctrl ] ';
  end;

  Panel1.Caption := S;
end;

end.


キーの押下状態は,次のように,GetKeyState 関数でも調べることができます.


procedure TForm1.Button1Click(Sender: TObject);
var
  S  : String;
begin
  S := '';

  if (GetKeyState(Ord('A')) and (1 shl 15)) <> 0 then S := S + ' [ A ] ';

  if (GetKeyState(VK_MENU)    and (1 shl 15)) <> 0 then S := S + ' [ Alt ] ';
  if (GetKeyState(VK_SHIFT)   and (1 shl 15)) <> 0 then S := S + ' [ Shift ] ';
  if (GetKeyState(VK_CONTROL) and (1 shl 15)) <> 0 then S := S + ' [ Ctrl ] ';

  Panel1.Caption := S;
end;


GetKeyState 関数の戻り値は SmallInt 型 (16 ビット整数) です.上のコードは 16 ビットの最上位ビットだけが 1 であるかを検査しています.したがって,次のようにも書けます.


procedure TForm1.Button1Click(Sender: TObject);
var
  S : String;
begin
  S := '';

  if GetKeyState(Ord('A')) and $8000 <> 0 then S := S + ' [ A ] ';

  if GetKeyState(VK_MENU)    and $8000 <> 0 then S := S + ' [ Alt ] ';
  if GetKeyState(VK_SHIFT)   and $8000 <> 0 then S := S + ' [ Shift ] ';
  if GetKeyState(VK_CONTROL) and $8000 <> 0 then S := S + ' [ Ctrl ] ';

  Panel1.Caption := S;
end;


GetAsyncKeyState 関数でもキーの押下状態を調べることができます.この 関数の戻り値は SmallInt 型 (16 ビット整数) です.この関数の戻り値の最上位ビットだけが 1 であるかを検査します.


procedure TForm1.Button1Click(Sender: TObject);
var
  S : String;
begin
  S := '';

  if GetAsyncKeyState(Ord('A')) and $8000 <> 0 then S := S + ' [ A ] ';

  if GetAsyncKeyState(VK_MENU)    and $8000 <> 0 then S := S + ' [ Alt ] ';
  if GetAsyncKeyState(VK_SHIFT)   and $8000 <> 0 then S := S + ' [ Shift ] ';
  if GetAsyncKeyState(VK_CONTROL) and $8000 <> 0 then S := S + ' [ Ctrl ] ';

  Panel1.Caption := S;
end;




03_汎用ビットフィールド操作レコード


汎用ビットフィールド操作レコード (TBitFieldRec) を使用すると,これまでのビット操作あるいは他のビット操作を簡単なコードで実装できます.

汎用ビットフィールド操作レコードは,以下のリンク記事のページからダウンロードできます.そのページにはサンプルもありますので,ここでは,掲示板等でたまにある「数値を 2 進数にするにはどうしたらいいのか」という質問に対するサンプルだけとしています.
コンピュータの内部の演算は全て 2 進数ですが,プログラムのコード中に 2 進数としての数値は直接指定できません.したがって,実際には「2 進数の値」ではなく,2 進数の文字列表示です.


図5
実行結果
  • 上から順番に
    10 進数表記
    16 進数表記
     2 進数表記

リスト3
TBitFieldRecを使用して,数値をいろいろな進数で表示
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Panel1: TPanel;
    Panel2: TPanel;
    Panel3: TPanel;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses uBitField;

{$R *.DFM}

//=============================================================================
//  数値をいろいろな進数で表示
//  TBitFieldRecを利用
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  BitField : TBitFieldRec;
begin
  BitField := $A5;

  //10進数文字列
  Panel1.Caption := BitField.ToString + ' ';

  //16進数文字列(8桁で表示)
  Panel2.Caption := BitField.ToHexString(8) + ' ';

  //2進数文字列(16桁で表示)
  Panel3.Caption := BitField.ToBinString(16) + ' ';
end;

end.