はじめに
PHP で非常に大きな数値を扱う場合、標準の整数型では桁あふれの問題が発生します。GMP(GNU Multiple Precision)拡張モジュールは、そんな状況で威力を発揮します。今回は GMP 拡張モジュールの中でも特に便利な gmp_div_qr
関数について詳しく解説します。この関数は、除算の「商」と「余り」を一度に取得できる効率的な関数です。
gmp_div_qr 関数とは?
gmp_div_qr
関数は、多倍長整数の除算を行い、商と余りの両方を一度に返す関数です。gmp_div_q
(商のみ)や gmp_div_r
(余りのみ)と異なり、1回の関数呼び出しで両方の値を取得できる点が大きな特徴です。
基本的な構文
gmp_div_qr(GMP $num1, GMP $num2, int $rounding_mode = GMP_ROUND_ZERO): array
$num1
: 被除数(割られる数)$num2
: 除数(割る数)$rounding_mode
: 丸めモード(オプション、デフォルトは GMP_ROUND_ZERO)- 戻り値: 商と余りを含む配列
[商, 余り]
丸めモードについて
gmp_div_qr
では、除算結果の丸め方を指定できます:
GMP_ROUND_ZERO
: ゼロ方向への切り捨て(デフォルト)GMP_ROUND_PLUSINF
: 正の無限大方向への切り上げGMP_ROUND_MINUSINF
: 負の無限大方向への切り下げ
使用例
基本的な使い方
<?php
// 25を7で割る
$a = gmp_init("25");
$b = gmp_init("7");
$result = gmp_div_qr($a, $b);
echo "商: " . gmp_strval($result[0]) . "\n"; // 出力: 3
echo "余り: " . gmp_strval($result[1]); // 出力: 4
?>
丸めモードによる違いの例
<?php
// -25を7で割る(丸めモードによる違いを確認)
$a = gmp_init("-25");
$b = gmp_init("7");
// ゼロ方向への切り捨て
$result1 = gmp_div_qr($a, $b, GMP_ROUND_ZERO);
echo "GMP_ROUND_ZERO:\n";
echo " 商: " . gmp_strval($result1[0]) . "\n"; // -3
echo " 余り: " . gmp_strval($result1[1]) . "\n\n"; // -4
// 正の無限大方向への切り上げ
$result2 = gmp_div_qr($a, $b, GMP_ROUND_PLUSINF);
echo "GMP_ROUND_PLUSINF:\n";
echo " 商: " . gmp_strval($result2[0]) . "\n"; // -3
echo " 余り: " . gmp_strval($result2[1]) . "\n\n"; // -4
// 負の無限大方向への切り下げ
$result3 = gmp_div_qr($a, $b, GMP_ROUND_MINUSINF);
echo "GMP_ROUND_MINUSINF:\n";
echo " 商: " . gmp_strval($result3[0]) . "\n"; // -4
echo " 余り: " . gmp_strval($result3[1]) . "\n"; // 3
?>
実践的な活用例:大きな数値の除算
暗号計算や精密な数学計算で非常に大きな数値を扱う場合の例:
<?php
// 非常に大きな数値の除算
$large_number = gmp_init("98765432109876543210987654321");
$divisor = gmp_init("123456789");
$result = gmp_div_qr($large_number, $divisor);
echo "商: " . gmp_strval($result[0]) . "\n";
echo "余り: " . gmp_strval($result[1]);
// 検証:元の数値 = 商 × 除数 + 余り
$verification = gmp_add(gmp_mul($result[0], $divisor), $result[1]);
echo "\n\n検証結果: " . gmp_strval($verification);
// 元の数値と一致するはず
?>
パフォーマンスの利点
gmp_div_qr
は、gmp_div_q
と gmp_div_r
を別々に呼び出すよりも効率的です。内部的には1回の計算で商と余りの両方を求めるため、処理速度の向上が期待できます:
<?php
// 大きな数値を処理する場合
$a = gmp_init("987654321098765432109876543210");
$b = gmp_init("12345678");
// 方法1: gmp_div_qr を使用(効率的)
$start = microtime(true);
$result = gmp_div_qr($a, $b);
$q1 = $result[0];
$r1 = $result[1];
$time1 = microtime(true) - $start;
// 方法2: 個別に計算(非効率的)
$start = microtime(true);
$q2 = gmp_div_q($a, $b);
$r2 = gmp_div_r($a, $b);
$time2 = microtime(true) - $start;
echo "gmp_div_qr 処理時間: " . $time1 . " 秒\n";
echo "個別計算処理時間: " . $time2 . " 秒\n";
echo "速度向上率: " . (($time2 / $time1) - 1) * 100 . "%";
?>
注意点
- ゼロによる除算: 除数が 0 の場合、警告が発生し処理が中断されます。
- GMP オブジェクト: PHP 7.2 以降では、GMP リソースではなく GMP オブジェクトが使用されます。
- 戻り値の配列: 返される配列は常に2つの要素を持ち、インデックス0が商、インデックス1が余りです。
- 丸めモードの影響: 特に負の数を扱う場合、丸めモードによって結果が大きく変わることがあります。
関連する GMP 除算関数との違い
関数名 | 戻り値 | 特徴 |
---|---|---|
gmp_div_q | 商のみ | 余りは捨てる |
gmp_div_r | 余りのみ | 商は捨てる |
gmp_div_qr | 商と余り | 両方を一度に計算(効率的) |
gmp_div | 商のみ | gmp_div_q のエイリアス |
gmp_mod | 余りのみ | 常に非負の余りを返す |
まとめ
gmp_div_qr
関数は、PHP で大きな整数の除算を行う際に、商と余りを効率的に一度に取得できる強力なツールです。暗号計算、科学計算、精密な金融計算など、多倍長整数を扱う場面で特に役立ちます。
丸めモードを適切に選択することで、異なる除算の振る舞いを制御でき、様々な要件に対応できます。パフォーマンスを重視するアプリケーションでは、個別に商と余りを計算するよりも gmp_div_qr
を使用することで処理効率が向上します。
大きな整数の演算が必要な際は、GMP 拡張モジュールとその関数群、特に効率的な gmp_div_qr
関数を積極的に活用しましょう。