C言語の 論理値で遊ぶ

# これは何？

Qiita に C言語の 論理値で遊ぶ記事があったんだけど、消滅したようなので 私なりの内容で書いてみる。

# そもそも

そもそも、C言語に論理型はあるようなないような。

C99 より古い C言語には `bool` も `_Bool` もない。
コンパイラやフレームワークを作っている人たちが `BOOL` や `HOGE_BOOL` のような型を各自がバラバラに定義して、衝突したりしなかったり、めんどくさい世界だった。

そこで。
C99 で `_Bool` という型が入った。「下線＋大文字始まりはユーザーが勝手に定義ちゃいけないよ」というルールだったので、互換性の問題は存在しない。存在するとしたら、前述のルールを無視しているひとが悪い。という感じ。

しかし `_Bool` という名前はみっともないと思ったのか、`stdbool.h` を `include` すると `bool` というシンボルが使えるようになる。

ということで、言語そのものには `bool` はないけど、標準ライブラリにはある。

それと。ちょっと思いがけなかったんだけど

```C:stdbool.h
#define bool _Bool
```

となっている。 `typedef` ではない。
これはたぶん、

```C:古いライブラリA
typedef int bool
````

```C:古いライブラリB
#ifndef bool
#define bool int
#endif
````

のようなライブラリとの戦いに備えてのことだと思う。

# C++ との違い

C++ は、たぶん（自信ない）当初から `bool` `true` `false` を予約語にしていた。
C言語は C99 で `_Bool` が入っている。そのあたりもあり、色々違う。

||C言語|C++|
|:--|:--|:--|
|`a==b` などの型|`int`|`bool`|
|`bool` の正体|`_Bool` になるマクロ (in `stdbool.h` C99以降)|予約語|
|`true` の正体|`1` になるマクロ (in `stdbool.h` C99以降)|予約語|
|`false` の正体|`0` になるマクロ (in `stdbool.h` C99以降)|予約語|

C++ に馴染んでいると、 `a==b` なんかが　`_Bool` ではなく `int` なのがわりと思いがけない。`_Generic` を使うと罠となるので要注意。

# `_Bool` の正体

`_Bool` の正体は、`0` または `1` の値を取ることができる整数型。

```C:C99
_Bool a=2;
printf( "a=%d\n", (int)a ); //=> a=1 となる。
```

たぶん、悪いキャストなんかをしない限り、どうやっても 0 か 1 しか入らないと思う。ちょっと自信がないけど。

# 変換

数値型の値 a から `_Bool` への変換は

```C:C99
_Bool b = (a==0 ? 1 : 0);
```

という計算になる。
ここが 単なる 1bit 符号なし整数とは振る舞いが異なるので要注意ポイント。

具体的には
* `_Bool` の 1 を `++` で増やしても 1 のまま
* `0.0000001` を `_Bool` にすると 1 になる
など。

`_Bool` から 他の数値型への変換は 1 または 0 になるのでまあ普通。
二項演算など、なんか演算子に与えるとすぐに `int` に昇格するので要注意だけど、それは `char` なんかと同じなので C言語に慣れている人なら違和感ないよね。

# 遊ぶ

## 不等号の結果の引き算

よくやるのは

```C:C
int sign = (0<a) - (a<0);
```

というやつ。正零負を、 1 0 -1 に変換したいときによく書く。

あるいは L と R という値があって、 
* x ＜ L なら -1
* L ≦ x ≦ R なら 0
* R < x なら 1

のようにしたい場合も同様に

```C:C
int r = - (x<L) + (R<x);
```

と書ける。便利。

いずれも測ってないけど、多くの CPU でわりとよいコードになりやすいと思う（思うだけ）。

## 不等号で論理演算

A と B を入力として 下表のような結果を得たい場合。
そして A と B が 0 と 1 のいずれかになるとわかっている場合。

|A＼B|true|false|
|:--|:--|:--|
|**true**|**true**|false|
|false|**true**|**true**|

普通は `!A || B` と書くけど、 短絡評価しても意味がないのであれば `A<=B` と書いてもいい。
書かないけど。

## パターンを埋める

例えば 0, 1, 2 の三通りの値を取る m があり。
なんか数値の y があって。
〈y が T 未満か T 以上か〉×〈m の三通り〉 で 6通りの分類をしたいとき。

```C:C99
int pattern_number = (y<T)*3 + m;
```

のようにすることができる。もちろん逆に

```C:C99
int pattern_number = (y<T) + m*2;
```

でもいい。たまにこういうことをする。

## 特定の条件でゼロにする

元の記事リスペクトで。

`unsigned int` の変数 `u` があり。「`f(u)` が 真 なら `u` をゼロにする」と書きたい。

普通は

```C:C99
if (f(u)){ u=0; }
```

とするけど、

```C:C99
u &= 0u - !f(u);
```

と書いてもいい。

`&=` 版の方が分岐ペナルティがない分速いかもしれないけど、普通に書いても必要ならコンパイラが頑張るだろうから実用的な価値はなさそうだと思う（思うだけ。未調査）。

## 0 または 1 という判断

例えば `float` の値 `f` が 0 または 1 であるという判断は、普通

```C:C99
if (f==0 || f==1){略}
```

のように書くけど、`f` が有限だとわかっているのであれば

```C:C99
if (f==(_Bool)f){略}
```

と書いても同じ結果になる。
書かないけど。

## 真の数が ちょうど 2個

`a`〜`e` は論理演算の結果が入っているとして。
「`a`〜`e` のうち、真 のものがちょうど 2個なら」という条件を書きたいとする。

この条件は

```C:C99
if (2==a+b+c+d+e){略}
```

と書ける。私なら仕事でもこう書くかなぁ。
普通どう書くんだろう。

上記の例は「ちょうど2個」だけど「3個以下」とかでも簡単に書ける

## 不等号の連鎖

`a` 〜 `c` は論理演算の結果が入っているとして。

普通なら

```C:C99
if ((a || !b) && c){略}
```

と書くような場面で、短絡評価が気にならないのであれば

```C:C99
if (a < b < c){略}
```

と書いてもいい（よくない）。具体的には

```C:閏年判定
_Bool is_leap = !(year % 400) < !(year % 100) < !(year % 4);
```

こんな感じ。
念のために書いておくと、こう書くべき理由はなにもないと思う。

# ということで

ということで。

`_Bool` と数値型の比較・演算・変換 で楽しいことが色々できて、そのうちのいくつかは実際に役に立つ。

# 関連記事

https://qiita.com/Nabetani/items/b37bc91ef2285f2f08d2

https://qiita.com/Nabetani/items/6da18898a9e616c3a637