intdivで整数除算を素直に書く

intdivは、整数同士の割り算の商を必ずint型で返す関数です。/演算子のように結果がfloatになることがなく、余りを切り捨てた整数の商を素直に書けます。

/ は割り切れてもfloatを返すことがある

PHP の / 演算子は、割り切れる場合でも文脈によっては float を返します。10 / 2int(5) ですが、10 / 3 は当然 float ですし、割り算の結果を整数として扱いたいのに型がぶれる、という気持ち悪さがあります。厳密な型宣言をしている関数に渡すと、ここで型エラーになることもありますね。

var_dump(10 / 2); // int(5)
var_dump(10 / 3); // float(3.3333...)
var_dump(9 / 3);  // int(3)

// これまではキャストで整数化していた
$pages = (int)(($total + $per - 1) / $per);

(int) キャストは小数点以下を切り捨てるだけなので、負の数だと 0 方向へ丸められる点も地味に注意が要ります。

intdivなら結果は必ずint

整数同士の商が欲しいなら intdiv() が素直です。返り値は必ず int で、余りは捨てられます。ページ数計算やチャンク分割のような「何個に分かれるか」を出すときに、キャストを挟まず書けるのが気持ち良いです。

var_dump(intdiv(10, 3)); // int(3)
var_dump(intdiv(-7, 2)); // int(-3)  0方向への切り捨て

// ページ数(切り上げ)を intdiv で
$pages = intdiv($total + $per - 1, $per);

ゼロ除算は DivisionByZeroError を投げます。/ の場合も PHP 8 以降は同じく例外なので、割る数が 0 になり得るなら事前チェックを入れておくと安心ですね。

剰余は%とfmodを使い分ける

余りが欲しいときは % 演算子ですが、これはオペランドを int に変換してから計算します。小数の剰余が欲しい場合は fmod() を使う必要があります。% に float を渡すと切り捨てられてしまうので、意図しない結果になりがちです。

var_dump(10 % 3);        // int(1)
var_dump(fmod(10.5, 3)); // float(1.5)

// intdiv と % はセットで使うと分かりやすい
$q = intdiv(100, 7); // 14
$r = 100 % 7;        // 2
echo "{$q} あまり {$r}";

まとめ

整数の商が欲しいのに float が返ってくる、という小さな気持ち悪さは intdiv() で解消できます。結果が必ず int になるのでキャストが要らず、意図も読み取りやすいです。余りは整数なら %、小数を含むなら fmod()、と分けるのが正解ですね。どちらもゼロ除算で例外が飛ぶので、割る数の検証だけは忘れないようにしたいところです。

よくある質問

Q. intdivと%はどう使い分けますか。
A. intdivは商(何回割れるか)、%は余りを返します。両方欲しいときは intdiv($a, $b) と $a % $b をセットで使うと分かりやすいです。

Q. 小数の余りを求めたいときは。
A. %はオペランドをintに変換してしまうので、小数の剰余には fmod() を使います。

Q. 0で割るとどうなりますか。
A. intdivはゼロ除算で DivisionByZeroError を投げます。割る数が0になり得る場合は事前にチェックしておくと安心です。

類似投稿

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です