Zig言語の一風変わった算術演算子
最近ホットな話題に便乗して 一部で注目を集めはじめたプログラミング言語Zigの言語仕様を眺めていたところ、他のプログラミング言語では見かけない一風変わった算術演算子が提供されるようです。
足し算3種盛
いわゆる「足し算」を行う算術加算について、Zig言語では 3種類の演算子 が提供されます。
-
+
: 通常の算術加算。結果が表現範囲を超える場合、プログラムは未定義動作となる。ビルドモードによってはエラーを報告してプログラム停止する。 -
+%
: 循環(Wrapping)加算。結果が表現範囲を超える場合、値を2の補数表現とみなして下位ビットを代入する。 -
+|
: 飽和(Saturating)加算。結果が表現範囲を超える場合、その整数型の最大値を代入する。
例えば8bit符号無し整数型(u8
)/値域[0, 255]
における足し算では、それぞれ次の演算結果が得られます。
1 + 2; // 3
1 +% 2; // 3
1 +| 2; // 3
128 + 128; // エラー
128 +% 128; // 0 (==256 % 256)
128 +| 128; // 255
足し算以外の算術演算に対しても同様に、あまり見慣れない演算子群が提供されます。また、それぞれに複合代入演算子(@
に対する@=
)も提供されます。[1]
- 加算:
+%
,+|
,+%=
,+|=
- 減算:
-%
,-|
,-%=
,-|=
- 乗算:
*%
,*|
,*%=
,*|=
- 単項マイナス:
-%
- 左ビットシフト:
<<|
,<<|=
未定義動作による最適化
大半のプログラミング言語ではZig言語でいう+
と+%
を区別せずに「加算演算子+
」として提供しますし、飽和加算+|
は言語組込ではなくライブラリ機能として提供されることが一般的でしょう。
Zig言語公式ドキュメント "In-depth Overview" によると、これは 未定義動作(Undefined Behavior)に基づくコード生成最適化 を目的とした言語デザインのようです。
Zig uses undefined behavior as a razor sharp tool for both bug prevention and performance enhancement.
Speaking of performance, Zig is faster than C.
- [...]
- Carefully chosen undefined behavior. For example, in Zig both signed and unsigned integers have undefined behavior on overflow, contrasted to only signed integers in C. This facilitates optimizations that are not available in C.
- [...]
実はこの「符号無し整数型オーバーフローも未定義動作とみなす」仕様は、かの C/C++言語仕様よりもストイック といえます。C/C++では符号付き整数型のオーバーフローは未定義動作としていますが、符号無し整数型オーバーフローはWrappingされることを保証します。他のプログラミング言語では符号有無によらず整数型オーバーフロー=Wrappingと規定することが一般的です。
Zig言語では全ての起こりうる未定義動作のリストアップを目指しており、可能な限りコンパイル時に、遅くともプログラム実行時には未定義動作を検出してエラー停止する思想になっています。
で、未定義動作って何なのよ?
(このセクションはC/C++の未定義動作にあまり馴染みのない方向けの補足説明です。)
Zig言語における「未定義動作」とは、C/C++同様に動作結果が一切定義されないことを意味します。ここでは「何らかのそれっぽい結果が得られること」さえ保証しないことに留意してください。プログラマが納得するか否かに関わらず、未定義動作を含むプログラムでは「鼻から悪魔が出てくる」ことも許容します。
...まぁこれはものの例えですから、現実主義なプログラマ向けの説明としては「30億のデバイスでは動くのに顧客環境でのみ動かない」や「偶発的にしか事象再現しない致命的なデータ破壊」を引き起こす可能性がある程度でしょう:P
翻ってコンパイラの観点に立つと、プログラムが未定義動作を含まないことを前提として積極的なコード生成最適化を行えるというメリットがあります。未定義動作に対してコンパイラは何をしても良いため、ある処理が未定義動作を引き起こすエッジケースは無視してしまい、本質的な処理のみに着目した簡素なコード生成を行えるケースがあります。
未定義動作と最適化の関係について解説した、分かりやすい翻訳記事「Old New Thing: 未定義動作はタイムトラベルを引き起こす(他にもいろいろあるけど、タイムトラベルが一番ぶっ飛んでる)」を参照ください。
チラシの裏
-
単項マイナス(
-
)や左ビットシフト(<<
)は算術演算子に区分されませんが、本記事の主題ではないため大目に見てください。 ↩︎
Discussion