skip to content

In PHP, how do intdiv(), the % operator, fmod() and fdiv() differ in result type, sign and division by zero?

level: middleimportance: nice to knowfreq 24%

answer

  1. intdiv: int, truncates toward zero
  2. % converts operands to int
  3. remainder takes the dividend's sign
  4. fmod(x, 0) is NAN
  5. fdiv returns INF instead of throwing

basics

~20 s

intdiv() 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
<?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

for a junior

Recall that intdiv() gives a whole-number quotient, % gives an integer remainder, and dividing by zero with them throws DivisionByZeroError.

for a middle

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.

for a senior

Choose between throwing integer operations and IEEE float operations deliberately, and validate INF and NAN before results reach storage or output.

for a principal

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.