Pascal (Delphi) において 1 > 0 という式は、どのように評価されるのか？

# はじめに
Pascal において `1 > 0` という式は、どのように評価されるでしょうか？

 - -1
 - 0
 - 1
 - その他
 
# 解答＆関連する話

答えは「その他」です。

Pascal の関係演算子 (`=`, `<>`, `<=`, `<`, `>`, `>=`, **in**) は論理型の結果を返すため、`1 > 0` の場合には **True** となります。

```pascal:EVTEST.pas
program EVTEST(Output);
begin
  Writeln(1>0);
end. 
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/8f3ae412-4b4c-3af8-ae28-ede913d6291e.png)

**See also:**

 - [演算子 (DocWiki)](https://docwiki.embarcadero.com/RADStudio/ja/%E5%BC%8F%EF%BC%88Delphi%EF%BC%89#.E6.BC.94.E7.AE.97.E5.AD.90)

## 比較結果を数値で返したい場合
結果を数値で返したい場合には、論理型を含めた順序型の順序値を返す標準関数 `Ord()` を使います。Delphi だと `Integer(1>0)` のようにも書けますが、これは型キャストなので個人的には好きではありません (書かない事もないけれど)。

```pascal:EVTEST.pas
program EVTEST(Output);
begin
  Writeln(Ord(1>0)); { Return ordinal value }
end. 
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/846f7bb3-4062-2f58-a154-d19981db94de.png)
```pascal:EVTEST.dpr
program EVTEST;
{$AAPPTYPE CONSOLE}
begin
  Writeln(Integer(1>0)); // 型キャスト
end. 
```

**See also:**

 - [System.Ord (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.Ord)

## 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:**

 - [System.ByteBool (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.ByteBool)
 - [System.WordBool (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.WordBool)
 - [System.LongBool (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.LongBool)

## Delphi のビット論理演算
Delphi で特定のビットが立っているのかを調べるのはちょっと面倒です。次のような書き方 (バイトの最上位ビットが立ってるか調べる) ができないのは、**and** の項 (オペランド) が論理型ではないためビット論理演算となり、結果が項の型 (例の場合 byte) で返るからです。

```pascal:EVTEST.dpr
program EVTEST;
{$AAPPTYPE CONSOLE}
begin
  if $7F and $80 then // NG
    Writeln('Set')
  else
    Writeln('Not set.');
end. 
```

Pascal の **if** 文における評価式は論理式です。式は論理型を返す必要があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/46b31b9f-a82c-231c-cfe6-a7b508064756.png)
Delphi には項 (オペランド) の型によって異なる動作をする演算子があります。ビット論理演算を使って特定のビットが立っているのかを調べるには次のようなコードになります。

```pascal:EVTEST.dpr
program EVTEST;
{$AAPPTYPE CONSOLE}
begin
  if $7F and $80 = $80 then // OK
    Writeln('Set')
  else
    Writeln('Not set.');
end. 
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/490a15a6-51a5-6526-f6a1-83b0d4d58774.png)
もしくは先述の `ByteBool` / `WordBool` / `LongBool` を使います。

```pascal:EVTEST.dpr
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** となる型なので、次のような書き方と同じ事になります。

```pascal:EVTEST.dpr
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 です。|

非効率ではありますが、次のようなコードも書けるようになった、という事です。

```pascal:EVTEST.dpr
program EVTEST;
{$APPTYPE CONSOLE}
begin
  if CountPopulation32($7F and $80) <> 0 then
    Writeln('Set.')
  else
    Writeln('Not set.');
end.
```

:::note warn
2022/09/01 時点では上記ビットカウント標準関数のドキュメントがありません。
:::

**See also:**

 - [Delphi 10.4におけるランタイムライブラリの強化 (blogs.embarcadero.com)](https://blogs.embarcadero.com/ja/coming-delphi-10-4-runtime-library-enhancements-ja/)
 - [ビットフィールドを操作する (Delphi 2006 以降...フル機能は 2009 以降) (ht-deko.com)](https://ht-deko.com/tech076.html)
 - [整数の中で立っているビットの数を数える (Owl's perspective)](http://owlsperspective.blogspot.com/2016/02/population-count.html)


## Pascal のビット論理演算
標準 Pascal にはビット論理演算子はありません。つまり、次のような記述はできません。

```pascal:EVTEST.dpr
program EVTEST(Output);
begin
  if 127 and 128 <> 0 then // NG
    Writeln('Set.')
  else
    Writeln('Not set.');
end.
```

:::note warn
標準 Pascal では 16 進数を表す事ができないので、10 進数を使っている事に注意してください。
:::

強いて言えば `集合 (Sets)` が抽象化されたビット配列です。

```pascal:SetsTest.pas
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 ではビット操作ルーチンが追加されています。

```Pascal
{ MPW Pascal }
BAND()		BOR()		BXOR()		BNOT()
BSL()		BSR()		BRotL()		BRotR()
BTst()		HIWrd()		LOWrd()		BClr()
BSet()
```

```Pascal
{ THINK Pascal }
BitAnd()	BitOr()		BitXor()	BitNot()
BAND()		BOR()		BXOR()		BNOT()
BSL()		BSR()		BRotL()		BRotR()
BTst()		HiWord()	HIWrd()		LoWord()
LOWrd()		BClr()		BSet()
```

:::note info
Pascal の後継言語である Modula-2 には BITSET (ビット集合) 型があり、16進値を示すポストフィクス (`H`) があります。
:::

**See also:**

 - [<8> 集合型 (標準 Pascal 範囲内での Delphi 入門)](https://qiita.com/ht_deko/items/50bac0de384d66208944)

# おわりに
この記事のそもそもの元ネタはこちらです。

 - [Welcome to John Regehr's Integers in C quiz](https://www.acepace.net/integerQuiz/)

C 言語の int 型がどういう振る舞いをするのかを当てるクイズです。本記事はこのクイズの問１を Pascal (Delphi) 向けにしたものです。