In PHP, how do intdiv(), the % operator, fmod() and fdiv() differ in result type, sign and division by zero?
answer
- intdiv: int, truncates toward zero
- % converts operands to int
- remainder takes the dividend's sign
- fmod(x, 0) is NAN
- fdiv returns INF instead of throwing
basics
~20 sintdiv() and % work on ints, truncate toward zero and throw DivisionByZeroError for a zero divisor. fmod() gives a float remainder and returns NAN for zero. fdiv() (PHP 8.0) performs IEEE float division, returning INF, -INF or NAN instead of throwing.
solid answer
~40 s`intdiv(int $num1, int $num2): int` is integer division **truncated toward zero**: `intdiv(-7, 2)` is `-3`. It throws `DivisionByZeroError` for a zero divisor and `ArithmeticError` for `intdiv(PHP_INT_MIN, -1)`. The `%` operator converts both operands to `int` first — a fractional float such as `7.5` is truncated with a PHP 8.1+ deprecation — and its result takes the **sign of the dividend**: `-7 % 2` is `-1`; a zero divisor throws `DivisionByZeroError: Modulo by zero`. `fmod(float, float): float` is the floating-point remainder, also signed like the dividend, so `fmod(7.5, 2)` is `1.5`, and `fmod($x, 0)` returns `NAN` rather than throwing. `fdiv(float, float): float`, added in PHP 8.0, divides by IEEE 754 rules: `fdiv(1, 0)` is `INF`, `fdiv(-1, 0)` is `-INF`, `fdiv(0, 0)` is `NAN`.
code
php · 11 lines<?php
var_dump(intdiv(-7, 2)); // int(-3): toward zero
var_dump(-7 % 2); // int(-1): sign of the dividend
var_dump(fmod(-7.5, 2)); // float(-1.5)
var_dump(fdiv(1, 0)); // float(INF)
try {
echo 10 % 0;
} catch (DivisionByZeroError $e) {
echo $e->getMessage(), PHP_EOL; // Modulo by zero
}go deeper
Recall that intdiv() gives a whole-number quotient, % gives an integer remainder, and dividing by zero with them throws DivisionByZeroError.
Explain truncation toward zero, the dividend-sign rule for % and fmod(), the 8.1 deprecation for float operands to %, and why fmod(0.3, 0.1) is not zero.
Choose between throwing integer operations and IEEE float operations deliberately, and validate INF and NAN before results reach storage or output.
Decide how numeric edge cases surface in an API: exceptions for invalid input, special float values for numeric pipelines, and where each is converted.
## Four tools, four contracts PHP has several ways to divide and take remainders, and they differ in three things interviewers probe: the **type** of the result, the **sign** of the result for negative operands, and what happens when the divisor is **zero**. | Function / operator | Operands | Result | Rounding / sign | Divisor 0 | |---|---|---|---|---| | `intdiv($a, $b)` | `int` | `int` | quotient truncated toward zero | `DivisionByZeroError` | | `$a % $b` | converted to `int` | `int` | remainder has the sign of `$a` | `DivisionByZeroError` ("Modulo by zero") | | `fmod($a, $b)` | `float` | `float` | remainder has the sign of `$a` | returns `NAN` | | `fdiv($a, $b)` | `float` | `float` | IEEE 754 division | `INF`, `-INF` or `NAN` | ## intdiv() `intdiv()` exists because PHP has no integer division operator: `7 / 2` is `3.5`. `intdiv(7, 2)` is `3`, and for negative operands it truncates **toward zero**, not down: `intdiv(-7, 2)` is `-3`, while `floor(-7 / 2)` is `-4.0`. It has one extra failure mode besides a zero divisor: `intdiv(PHP_INT_MIN, -1)` would be 2^63, which is not a representable `int`, so it throws `ArithmeticError` with `Division of PHP_INT_MIN by -1 is not an integer`. ## The % operator `%` is an **integer** operator. Both operands are converted to `int` before the operation: - a float with a fractional part is truncated, and since **PHP 8.1** that implicit, lossy conversion emits `Deprecated: Implicit conversion from float 7.5 to int loses precision`; - the result's sign follows the **dividend** (left operand): `7 % -2` is `1`, `-7 % 2` is `-1`. That sign rule surprises people who expect a mathematical modulo that is always non-negative. To wrap an index into `0..n-1` with possibly negative input, use `(($i % $n) + $n) % $n`. ## fmod() `fmod()` is the floating-point remainder: `$a - $q * $b` where `$q` is `$a / $b` truncated toward zero. Its sign also follows the dividend. Because it works on binary floats, decimal-looking inputs can give surprising answers: ```php <?php var_dump(fmod(7.5, 2)); // float(1.5) var_dump(fmod(-7.5, 2)); // float(-1.5) var_dump(fmod(0.3, 0.1)); // float(0.09999999999999998), not 0 var_dump(fmod(1, 0)); // float(NAN) ``` `0.3` and `0.1` are not exact in binary, and `0.3 / 0.1` is slightly under 3, so the truncated quotient is 2 and almost a whole `0.1` remains. ## fdiv() `fdiv()`, added in **PHP 8.0**, performs division exactly as IEEE 754 defines it for floats. The `/` operator throws `DivisionByZeroError` on a zero divisor; `fdiv()` never does: - `fdiv(1, 0)` returns `INF`; - `fdiv(-1, 0)` returns `-INF`; - `fdiv(0, 0)` returns `NAN`. That is useful in numeric code where infinity is a meaningful result, for example a ratio that should sort to the end, and where an exception would be the wrong control flow. The result then has to be checked with `is_finite()`, `is_infinite()` or `is_nan()` before it is displayed or stored. ## Choosing 1. **Whole-number quotient** (pages, batches): `intdiv()`, and guard the divisor. 2. **Remainder of integers** (every nth row, round-robin): `%`, remembering the sign rule. 3. **Remainder of floats** (angles, periodic measurements): `fmod()`, with a tolerance on the result. 4. **Float division where infinity is acceptable**: `fdiv()`, then validate the result. For money, none of these is right on floats; use integer minor units with `intdiv()` and `%`, or bcmath. ## Common mistakes - **Assuming floor division.** `intdiv(-7, 2)` is `-3`; code ported from a language where integer division floors will be off by one for negative inputs. - **Assuming a non-negative remainder.** `-1 % 5` is `-1`, which breaks array indexing and cyclic schedules unless normalised. - **Using `%` on prices.** `10.50 % 3` computes `10 % 3`, with only a deprecation notice, not a decimal remainder. - **Catching the wrong thing for `fmod()`.** It never throws; a zero divisor yields `NAN`, which then poisons every later calculation until someone checks `is_nan()`.
- In PHP, how do you get a non-negative index from $i % $n when $i can be negative?Normalise the remainder: `(($i % $n) + $n) % $n`. PHP's `%` gives the remainder the sign of the dividend, so `-1 % 5` is `-1`; adding `$n` and taking `%` again maps it into `0..$n-1`, giving `4`.
- In PHP 8, what does 7.5 % 2 return, and does PHP complain?It returns `int(1)`: `%` converts `7.5` to `7` before computing `7 % 2`. Since PHP 8.1 that lossy float-to-int conversion emits a deprecation notice, `Implicit conversion from float 7.5 to int loses precision`. Use `fmod(7.5, 2)`, which returns `1.5`, when you want a float remainder.
saying these in an interview costs you the question
- Says intdiv(-7, 2) rounds down to -4.
- Believes % always returns a non-negative result in PHP.
- Thinks fmod(1, 0) throws DivisionByZeroError.
- Expects % to work on floats like fmod().
- Uses fdiv() and stores the result without checking for INF or NAN.