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日に以下の記事を追加しました.それに伴い,本ページに掲載していたサンプルの一部を削除しました.
与えられた数値の指定ビットが立っているか,つまり,指定ビットが 1 であるかを検査するテストプログラムです.
この検査にはビット演算子を使用しています.例えば shr というビット演算子を使用した
という演算は,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 であるか)を検査
copy code
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 であるかを調べる例です.
copy code
// =============================================================================
// 指定した複数のビットだけが全て 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 ;
Windows API を使用する際,指定ビットが立っているか検査することがあります.
この場合,上のサンプルのように,ビットシフトの演算のコードを直接書いてもいいのですが,多くの場合,16 進数で論理積 (AND 条件)でコーディングするのが普通です.
次のサンプルは,ボタンをクリックした時に,[Shift] キー等を同時に押した状態でクリックしたかを検出するテストです.
GetKeyboardState 関数を使用していますが,この関数で取得した値の最上位ビットが 1 の場合,キーが押されていることを示しています.この引数の型は Byte 型ですから,8 ビットです.したがって,上のプログラムの結果から,8 ビット目だけが 1,つまり 2 進数の 10000000 というのは,16進数で 80 となります.そこで,この値との論理積で,キーの状態を調べることになります.
図3
設計時画面
図4
実行例
[Shift] キーと [Ctlr] キーを押したままボタンをクリックした場合
copy code
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 関数でも調べることができます.
copy code
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 であるかを検査しています.したがって,次のようにも書けます.
copy code
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 であるかを検査します.
copy code
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 ;
汎用ビットフィールド操作レコード (TBitFieldRec) を使用すると,これまでのビット操作あるいは他のビット操作を簡単なコードで実装できます.
汎用ビットフィールド操作レコードは,以下のリンク記事のページからダウンロードできます.そのページにはサンプルもありますので,ここでは,掲示板等でたまにある「数値を 2 進数にするにはどうしたらいいのか」という質問に対するサンプルだけとしています.
コンピュータの内部の演算は全て 2 進数ですが,プログラムのコード中に 2 進数としての数値は直接指定できません.したがって,実際には「2 進数の値」ではなく,2 進数の文字列表示です.
図5
実行結果
上から順番に
10 進数表記
16 進数表記
2 進数表記
リスト3
TBitFieldRecを使用して,数値をいろいろな進数で表示
copy code
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 .