PHPOffice/PhpSpreadsheet · error · PhpOffice\PhpSpreadsheet\Calculation\Exception
#NUM!
#NUM!
Error message
#NUM!
What it means
MathTrig\Helpers::validateNotNegative() (MathTrig/Helpers.php:59-66) is the domain guard for functions requiring a non-negative number: it throws #NUM! by default, or a caller-supplied $except error string. Callers include SQRT (Sqrt::funcSqrt), FACT/FACTDOUBLE (Factorial), GCD/LCM values, COMBIN/COMBINX counts, and RAND's max-min span (RANDBETWEEN with min > max).
Source
Thrown at src/PhpSpreadsheet/Calculation/MathTrig/Helpers.php:65
return $substitute;
}
if (is_numeric($number)) {
return 0 + $number;
}
throw new Exception(ExcelError::throwError($number));
}
/**
* Confirm number >= 0.
*/
public static function validateNotNegative(float|int $number, ?string $except = null): void
{
if ($number >= 0) {
return;
}
throw new Exception($except ?? ExcelError::NAN());
}
/**
* Confirm number > 0.
*/
public static function validatePositive(float|int $number, ?string $except = null): void
{
if ($number > 0) {
return;
}
throw new Exception($except ?? ExcelError::NAN());
}
/**
* Confirm number != 0.
*/
public static function validateNotZero(float|int $number): voidView on GitHub (pinned to 65b080eef4)
Solutions
- Check $value >= 0 before calling these functions
- Use abs() only when the domain genuinely means magnitude
- Supply min/max to RANDBETWEEN in the correct (min, max) order
- Validate numeric bounds at the input boundary, not inside spreadsheet formulas
Example fix
// before
$result = Sqrt::funcSqrt($a - $b); // negative when $b > $a -> '#NUM!'
// after: guard the domain, then choose a policy
$delta = $a - $b;
if ($delta < 0) {
throw new InvalidArgumentException('cannot take SQRT of a negative difference');
}
$result = Sqrt::funcSqrt($delta); Defensive patterns
Strategy: validation
Validate before calling
if ((float) $number < 0.0) {
throw new InvalidArgumentException('value must be non-negative for this function');
}
$result = Sqrt::funcSqrt($number); Type guard
function isNonNegativeNumber(mixed $value): bool
{
return is_numeric($value) && (float) $value >= 0.0;
} Try / catch
$result = Factorial::funcFact($value);
if ($result === '#NUM!' && (float) $value < 0.0) {
// negative input reached a non-negative-only domain (SQRT/FACT/GCD/LCM/COMBIN)
} Prevention
- Guard differences before SQRT: compute abs() only if magnitude is intended
- Order RANDBETWEEN arguments (min, max)
- Validate user-entered counts/durations as >= 0 at input time
When it happens
Trigger: SQRT(-1); FACT(-5); GCD(-12, 8); RANDBETWEEN(100, 50) where the negative span (max-min) reaches the guard.
Common situations: User-supplied quantities or durations that can go negative; square roots of differences (a-b with b > a); random ranges supplied in the wrong order; date/time deltas computed backwards.
Related errors
AI-assisted analysis of PHPOffice/PhpSpreadsheet@65b080eef4 (2026-08-17).
Data as JSON: /api/errors/e43e306b52b63d89.
Report an issue: GitHub.