こんにちは、高木です。
例によってPHPでC言語やC++の前処理を行う話題の続きです。といっても、今回は前処理に特化した話題というよりは純粋にPHPの話題に近いかもしれません。
C言語やC++の前処理を行うにあたって、初期化子などを自動生成させることは多いと思います。現在のC言語やC++では、当たり前のように64ビット整数を使いますし、処理系によっては__int128型が使えることもありますので、PHPで普通に扱える整数値の範囲を超えています。
ここでPHPで扱うことができる整数値についておさらいしておくことにします。PHPのint型の表現範囲はC言語と同じで環境依存です。32ビット環境であれば符合付き32ビット整数の範囲ですので-2,147,483,648~+2,147,483,647まで、64ビット環境であれば64ビット整数の範囲ですので-9,223,372,036,854,775,808~+9,223,372,036,854,775,807までとなります。演算結果がint型の表現範囲を超える場合、原則としてfloat型になってしまいます。float型の表現範囲も環境依存です。
このような状況ですので、32ビット環境では符合無し32ビット整数や64ビット整数を扱うことができませんし、64ビット環境でも符合無し64ビット整数を扱うことができません。64ビットを超える整数値なんか論外です。float型は64ビット以上あると考えてよさそうですので、32ビット環境で符合無し32ビット整数を扱う際でも算術演算だけならどうにかなりそうですが、ビット演算やシフト演算を行うとビックリするような結果になります。64ビット整数の演算を行うには64ビットのfloat型では仮数部の桁が足りません。
こんなとき頼りになるのがGMP関数です。GMP関数はC言語ではおなじみのGNU Multiple PrecisionのPHPバインディングです。GMP関数には整数演算で必要な算術演算のほか、ビット演算やシフト演算も備わっています。うれしいことにPHP 5.6からは、代入、比較、算術、ビット、シフト演算あたりの演算子がオーバーロードされています。GMP関数で扱う値を作るときだけgmp_init関数を読んであげれば、あとは普通の数値と同じように扱うことができます。
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<?php $a = gmp_init('12345'); $b = gmp_init('67890'); printf("%d\n", $a + $b); printf("%d\n", $a - $b); printf("%d\n", $a * $b); printf("%d\n", $a / $b); printf("%d\n", $a % $b); printf("%d\n", $a & $b); printf("%d\n", $a | $b); printf("%d\n", $a ^ $b); printf("%d\n", $a == $b); printf("%d\n", $a != $b); printf("%d\n", $a < $b); printf("%d\n", $a <= $b); printf("%d\n", $a > $b); printf("%d\n", $a >= $b); printf("%d\n", $a << 2); printf("%d\n", $a >> 2); printf("%d\n", +$a); printf("%d\n", -$a); printf("%d\n", ~$a); ?> |
これさえあれば、64ビット整数だろうが128ビット整数だろうが問題なく処理できそうです。とはいえ、普通のint型に比べて実行コストが大きいことは間違いないので、大量の演算が必要な場合はint型でできることはint型で行うようにすべきです。
一点注意しなければならないのは、GMP関数はデフォルト設定では使えませんので、明示的に設定してあげる必要があります。Windowsであればphp.iniでextension=gmpを有効にしてあげればOKです。Linuxでは拡張モジュールをインストールしてあげてください。Ubuntuではsudo apt install php8.1-gmpでインストールすることができました(PHPのバージョンは該当するものを指定してください)。