はじめに
Pascal において 1 > 0
という式は、どのように評価されるでしょうか?
- -1
- 0
- 1
- その他
解答&関連する話
答えは「その他」です。
Pascal の関係演算子 (=
, <>
, <=
, <
, >
, >=
, in) は論理型の結果を返すため、1 > 0
の場合には True となります。
program EVTEST(Output);
begin
Writeln(1>0);
end.
See also:
比較結果を数値で返したい場合
結果を数値で返したい場合には、論理型を含めた順序型の順序値を返す標準関数 Ord()
を使います。Delphi だと Integer(1>0)
のようにも書けますが、これは型キャストなので個人的には好きではありません (書かない事もないけれど)。
program EVTEST(Output);
begin
Writeln(Ord(1>0)); { Return ordinal value }
end.
program EVTEST;
{$AAPPTYPE CONSOLE}
begin
Writeln(Integer(1>0)); // 型キャスト
end.
See also:
Pascal の論理型の順序値
Pascal の論理型 (Boolean) は False, True という順序で並んでいるため、
- False = 0
- True = 1
となります。True は not False だから数値で -1
という考え方ではありません。"値で -1" というのは、たとえば 8bit だと not 0b00000000
= 0b11111111
= 0xFF
(unsigned) = -1
(signed)という考え方の事です。
「8bit と 16bit の -1 は同じ値なのか?」とか考えだすと、Boolean (bool) 型はあった方がいいよねって結論に至ると思います。
Delphi には他言語との互換性のために ByteBool
/ WordBool
/ LongBool
という True が -1 の順序値を返す型も用意されています。
See also:
Delphi のビット論理演算
Delphi で特定のビットが立っているのかを調べるのはちょっと面倒です。次のような書き方 (バイトの最上位ビットが立ってるか調べる) ができないのは、and の項 (オペランド) が論理型ではないためビット論理演算となり、結果が項の型 (例の場合 byte) で返るからです。
program EVTEST;
{$AAPPTYPE CONSOLE}
begin
if $7F and $80 then // NG
Writeln('Set')
else
Writeln('Not set.');
end.
Pascal の if 文における評価式は論理式です。式は論理型を返す必要があります。
Delphi には項 (オペランド) の型によって異なる動作をする演算子があります。ビット論理演算を使って特定のビットが立っているのかを調べるには次のようなコードになります。
program EVTEST;
{$AAPPTYPE CONSOLE}
begin
if $7F and $80 = $80 then // OK
Writeln('Set')
else
Writeln('Not set.');
end.
もしくは先述の ByteBool
/ WordBool
/ LongBool
を使います。
program EVTEST;
{$AAPPTYPE CONSOLE}
begin
if ByteBool($7F and $80) then // OK
Writeln('Set')
else
Writeln('Not set.');
end.
ByteBool
/ WordBool
/ LongBool
は 0 が False、それ以外が True となる型なので、次のような書き方と同じ事になります。
program EVTEST;
{$AAPPTYPE CONSOLE}
begin
if $7F and $80 <> 0 then // OK
Writeln('Set')
else
Writeln('Not set.');
end.
また、Delphi 10.4 では次に示す ビットカウント標準関数 が追加されています。
関数 | 説明 |
---|---|
System.CountLeadingZeros32() | パラメータ X で 1 を保持する最上位ビットより上位ビットにある 0 の数を返します。X が 0 の場合、ビット幅を返します。つまり、CountLeadingZeros32 では 32 を返します。 |
System.CountLeadingZeros64() | (同上) CountLeadingZeros64 では 64 を返します。 |
System.CountTrailingZeros32() | パラメータ X で 1 を保持する最下位ビットより下位ビットにある 0 の数を返します。X が 0 の場合、ビット幅を返します。つまり CountTrailingZeros32 では 32 を返します。 |
System.CountTrailingZeros64() | (同上) CountTrailingZeros64では 64 を返します。 |
System.CountPopulation32() | パラメータ X の 1 の数をカウントします。戻り値は、0 からビット幅の数字の範囲になります。つまり、CountPopulation32 では 32 です。 |
System.CountPopulation64() | (同上) CountPopulation64 では 64 です。 |
非効率ではありますが、次のようなコードも書けるようになった、という事です。
program EVTEST;
{$APPTYPE CONSOLE}
begin
if CountPopulation32($7F and $80) <> 0 then
Writeln('Set.')
else
Writeln('Not set.');
end.
2022/09/01 時点では上記ビットカウント標準関数のドキュメントがありません。
See also:
- Delphi 10.4におけるランタイムライブラリの強化 (blogs.embarcadero.com)
- ビットフィールドを操作する (Delphi 2006 以降...フル機能は 2009 以降) (ht-deko.com)
- 整数の中で立っているビットの数を数える (Owl's perspective)
Pascal のビット論理演算
標準 Pascal にはビット論理演算子はありません。つまり、次のような記述はできません。
program EVTEST(Output);
begin
if 127 and 128 <> 0 then // NG
Writeln('Set.')
else
Writeln('Not set.');
end.
標準 Pascal では 16 進数を表す事ができないので、10 進数を使っている事に注意してください。
強いて言えば 集合 (Sets)
が抽象化されたビット配列です。
program SetsTest(Output);
type
TBits = (bit0, bit1, bit2, bit3, bit4, bit5, bit6, bit7);
var
a, b: Set of TBits;
begin
a := [bit6, bit5, bit4, bit3, bit2, bit1, bit0]; { 0x7F }
b := [bit7]; { 0x80 }
if a*b <> [] then { a AND b <> 0x00 }
Writeln('Set.')
else
Writeln('Not set.');
end.
これでは不便だという事で、Turbo Pascal や Delphi ではビット論理演算子が追加され、Macintosh 系の Pascal ではビット操作ルーチンが追加されています。
{ MPW Pascal }
BAND() BOR() BXOR() BNOT()
BSL() BSR() BRotL() BRotR()
BTst() HIWrd() LOWrd() BClr()
BSet()
{ THINK Pascal }
BitAnd() BitOr() BitXor() BitNot()
BAND() BOR() BXOR() BNOT()
BSL() BSR() BRotL() BRotR()
BTst() HiWord() HIWrd() LoWord()
LOWrd() BClr() BSet()
Pascal の後継言語である Modula-2 には BITSET (ビット集合) 型があり、16進値を示すポストフィクス (H
) があります。
See also:
おわりに
この記事のそもそもの元ネタはこちらです。
C 言語の int 型がどういう振る舞いをするのかを当てるクイズです。本記事はこのクイズの問1を Pascal (Delphi) 向けにしたものです。