PHPOffice/PhpSpreadsheet · error · PhpSpreadsheet\Calculation\Exception
#NUM!
Error message
#NUM!
What it means
#NUM! from the places (padding width) validator shared by all base-conversion functions that take a places argument (BIN2HEX, BIN2OCT, DEC2BIN, DEC2HEX, DEC2OCT, HEX2BIN, HEX2OCT, OCT2BIN, OCT2HEX). ConvertBase::validatePlaces() rejects numeric places below 0 or above 10 with Calculation\Exception('#NUM!'), matching Excel's documented limit.
Source
Thrown at src/PhpSpreadsheet/Calculation/Engineering/ConvertBase.php:41
if (is_numeric($value)) {
if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
$value = floor((float) $value);
}
}
return strtoupper(StringHelper::convertToString($value));
}
protected static function validatePlaces(mixed $places = null): ?int
{
if ($places === null) {
return $places;
}
if (is_numeric($places)) {
if ($places < 0 || $places > 10) {
throw new Exception(ExcelError::NAN());
}
return (int) $places;
}
throw new Exception(ExcelError::VALUE());
}
/**
* Formats a number base string value with leading zeroes.
*
* @param string $value The "number" to pad
* @param ?int $places The length that we want to pad this value
*
* @return string The padded "number"
*/
protected static function nbrConversionFormat(string $value, ?int $places): string
{View on GitHub (pinned to 65b080eef4)
Solutions
- Keep places in the range 0..10 (it is truncated to int, so 3.9 becomes 3).
- For wider output, pad the returned string yourself with str_pad() after the conversion.
- Omit places (pass null) to get the minimum number of characters.
- Note nbrConversionFormat() additionally returns #NUM! when the result needs more digits than places allows.
Example fix
// before
$hex = ConvertBinary::toHex('10101010', 16); // '#NUM!' - places > 10
// after
$hex = str_pad(ConvertBinary::toHex('10101010'), 16, '0', STR_PAD_LEFT); Defensive patterns
Strategy: validation
Validate before calling
$places = ($places === null) ? null : (int) $places;
if ($places !== null && ($places < 0 || $places > 10)) {
throw new \InvalidArgumentException('places must be between 0 and 10');
}
$result = ConvertDecimal::toBinary($value, $places); Type guard
/** places must be null or an integer 0..10. */
function isValidPlaces(mixed $places): bool
{
return $places === null || (is_numeric($places) && $places >= 0 && $places <= 10);
} Prevention
- Outputs are capped at 10 characters - pad wider results yourself with str_pad().
- Omit places (null) when minimum width is acceptable.
- places is truncated, not rounded: 10.9 becomes 10, 10.99 is fine but 11 is not.
When it happens
Trigger: =BIN2HEX("1010", 11), =DEC2BIN(5, -1); ConvertHex::toBinary('1F', 12) from PHP; padding widths sourced from config that assume more than 10 output characters are possible (outputs are capped at 10 digits anyway).
Common situations: Wanting a fixed-width 16-character hex/binary output and passing 16; passing a negative 'unlimited' sentinel; computed widths from string length of the input plus padding.
Related errors
AI-assisted analysis of PHPOffice/PhpSpreadsheet@65b080eef4 (2026-08-17).
Data as JSON: /api/errors/03408783a3940169.
Report an issue: GitHub.