Integer overflow, PHP_INT_MAX, and GMP for big numbers
Concept
PHP integers are 64-bit signed on 64-bit systems (the only systems you'll encounter in production). The range is PHP_INT_MIN (-9,223,372,036,854,775,808) to PHP_INT_MAX (9,223,372,036,854,775,807). PHP has no 32-bit integers, no unsigned integers, and no overflow exceptions — overflow silently converts to float.
When integer overflow matters
For most web applications, integer overflow is a non-issue. But it matters for:
- Database IDs that use BIGINT: within PHP_INT_MAX range, safe
- Timestamps: Unix timestamps fit in 32 bits until year 2038 (the "Y2K38" problem), but PHP uses 64-bit even on 32-bit PHP builds since PHP 7
- Cryptographic operations: bit-level operations on large numbers
- Scientific computing: combinatorics, large factorials
- Financial systems: when storing as integer cents — 9.2 quadrillion cents is PHP_INT_MAX, so you won't overflow with money
The overflow behavior
$max = PHP_INT_MAX; // 9223372036854775807
$overflow = $max + 1; // float(9.2233720368548E+18) — silently became float
var_dump(gettype($overflow)); // "double" (float)GMP — GNU Multiple Precision
The gmp extension provides arbitrary-precision integers. GMP values are PHP objects wrapping C mpz_t (GNU MP integer type). Operations return new GMP objects.
$big = gmp_init('99999999999999999999999999999999');
$bigger = gmp_add($big, '1');
echo gmp_strval($bigger); // "100000000000000000000000000000000"GMP supports: add, sub, mul, div, mod, pow, factorial, gcd, lcm, primality testing, bit operations.
BCMath — Arbitrary Precision Decimal Math
The bcmath extension works with decimal strings and is designed for financial calculations. Unlike GMP (integers only), BCMath handles decimals with configurable precision.
bcscale(10); // 10 decimal places
echo bcadd('0.1', '0.2', 10); // "0.3000000000" — exact!
echo bcmul('19.99', '1.08', 2); // "21.59" — tax calculationFor financial applications: use BCMath for decimal money, or store as integer cents and use regular PHP arithmetic.
Code Example
<?php
declare(strict_types=1);
// Integer limits
echo PHP_INT_MAX . "\n"; // 9223372036854775807
echo PHP_INT_MIN . "\n"; // -9223372036854775808
echo PHP_INT_SIZE . "\n"; // 8 (bytes, so 64-bit)
// Overflow — silent conversion to float
$max = PHP_INT_MAX;
$overflow = $max + 1;
var_dump($overflow); // float(9.2233720368548E+18)
var_dump(is_float($overflow)); // true
var_dump(is_int($overflow)); // false
// You can detect potential overflow before it happens
function safeAdd(int $a, int $b): int
{
if ($b > 0 && $a > PHP_INT_MAX - $b) {
throw new \OverflowException("Integer overflow: {$a} + {$b}");
}
if ($b < 0 && $a < PHP_INT_MIN - $b) {
throw new \UnderflowException("Integer underflow: {$a} + {$b}");
}
return $a + $b;
}
// GMP for big integers
if (extension_loaded('gmp')) {
$n = gmp_init('9223372036854775807'); // PHP_INT_MAX as GMP
$n1 = gmp_add($n, 1);
echo gmp_strval($n1) . "\n"; // 9223372036854775808 — exact!
// Factorial — try this with regular PHP and you get INF
$fact20 = gmp_fact(20);
echo gmp_strval($fact20) . "\n"; // 2432902008176640000
$fact100 = gmp_fact(100);
echo gmp_strval($fact100) . "\n"; // 158 digits, exact
// Primality testing
var_dump(gmp_prob_prime(gmp_init('982451653')) > 0); // true
}
// BCMath for arbitrary precision decimals (financial math)
if (extension_loaded('bcmath')) {
// The classic float precision problem:
var_dump(0.1 + 0.2 === 0.3); // false — floating point!
var_dump(bcadd('0.1', '0.2', 10) === '0.3000000000'); // true
// Money calculation — 8.5% tax on $19.99
$price = '19.99';
$taxRate = '0.085';
$tax = bcmul($price, $taxRate, 4); // '1.6991'
$tax = bcadd($tax, '0.005', 2); // Round half-up: '1.70'
$total = bcadd($price, $tax, 2); // '21.69'
echo "Total: \${$total}\n";
}
// Integer literals — different bases
$decimal = 255;
$hex = 0xFF; // 255
$octal = 0377; // 255
$binary = 0b11111111; // 255
$readable = 1_000_000; // Underscore separator (PHP 7.4+) for readability
echo "$decimal $hex $octal $binary $readable\n";Interview Q&A
Q: PHP has no unsigned integers. What are the implications for working with large IDs or hash values?
The absence of unsigned integers means values that should be non-negative (like database IDs or bitfield results) can appear negative in PHP. For example, a MySQL BIGINT UNSIGNED can store values up to 18,446,744,073,709,551,615, but PHP's signed int only goes to 9,223,372,036,854,775,807 — values above PHP_INT_MAX arrive as float (losing precision) or as string. When working with unsigned 64-bit values from databases or binary protocols, use string representation and BCMath/GMP for comparisons. PHP's pack()/unpack() functions have J (unsigned 64-bit big-endian) and P (unsigned 64-bit little-endian) format codes that return values as strings to avoid signed overflow.
Q: When would you use GMP versus BCMath for big number arithmetic?
GMP handles arbitrary-precision integers efficiently using C's GNU MP library. It's fast, supports number theory functions (primality, GCD, modular exponentiation), and is ideal for cryptographic primitives, factorials, and large integer arithmetic. BCMath handles arbitrary-precision decimal numbers as strings, making it the right choice for financial calculations where exact decimal representation matters. bcadd('0.1', '0.2', 2) gives '0.30' exactly. BCMath is slower than GMP but handles fractions. For financial applications, the standard pattern is: store amounts as integer cents (BCMath for calculations), or use BCMath throughout if you need fractional units. GMP is for integer-only problems like RSA key generation, large primality tests, or working with very large integers.
Q: How does integer representation differ between 32-bit and 64-bit PHP, and should you care?
On 64-bit PHP (the only thing you'll see in production since 2015), integers are 64 bits wide — PHP_INT_SIZE = 8. On 32-bit PHP (legacy systems), integers are 32 bits — PHP_INT_MAX = 2,147,483,647. The Y2K38 problem (Unix timestamps overflowing a 32-bit int in 2038) affects 32-bit PHP but not 64-bit. In practice, you should care about this only when: writing portable libraries that claim to support 32-bit PHP (check PHP_INT_SIZE), handling database BIGINT values that might exceed 32-bit range, or doing bit manipulation on values wider than 32 bits. For modern applications deployed on 64-bit Linux servers, you can safely assume 64-bit integers and PHP_INT_MAX ≈ 9.2 × 10^18.