Math, Numbers and Randomness
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
This page is written against PHP 8.5 (with the bcmath and gmp extensions), checked on 2026-10-05; the clamp()
example was run on the PHP 8.6 release candidate. Integer and float types themselves are described in
Types and the Type System.
The math functions
The core math functions are always available:
| Function | Notes |
|---|---|
|
|
|
integer division rounding towards zero; throws |
|
float remainder (the |
|
IEEE 754 division: returns |
|
IEEE 754 exponentiation for floats (8.4); |
|
rounding to a precision (negative for tens, hundreds); |
|
always return |
|
the usual |
|
base conversion ( |
|
checks for special float values |
|
constants |
<?php
declare(strict_types=1);
var_dump(intdiv(7, 2), fmod(7.5, 2), fdiv(1, 0), fpow(2.0, 0.5)); // int(3) float(1.5) float(INF) float(1.414…)
// Rounding modes (8.4): default is HalfAwayFromZero
var_dump(round(2.5), round(-2.5), round(2.5, 0, RoundingMode::HalfEven), round(3.5, 0, RoundingMode::HalfEven));
// float(3) float(-3) float(2) float(4)
var_dump(round(1234.5678, -2), floor(-1.5), ceil(-1.5)); // float(1200) float(-2) float(-1)
var_dump(bindec('1010'), dechex(255), octdec('755'), base_convert('ff', 16, 2)); // 10 "ff" 493 "11111111"
var_dump(hypot(3, 4), is_nan(sqrt(-1))); // float(5) bool(true)
Source: Mathematical Functions, round and fpow
clamp() (upcoming in PHP 8.6)
clamp($value, $min, $max) limits a value to a range, replacing the max($min, min($max, $value)) idiom; it throws
ValueError if $min is greater than $max.
<?php
declare(strict_types=1);
// requires PHP 8.6
var_dump(clamp(15, 0, 10), clamp(-1, 0, 10), clamp(5.5, 0, 10)); // int(10) int(0) float(5.5)
Source: RFC: clamp() v2
Floating-point pitfalls with money
Floats are binary fractions: 0.1 cannot be represented exactly, so sums drift and rounding at a boundary depends on
invisible digits. Never store or compute money as floats. Store integer minor units (cents), or use arbitrary
precision decimals.
<?php
declare(strict_types=1);
var_dump(0.1 + 0.2); // float(0.30000000000000004)
var_dump(number_format(0.1 + 0.2, 20)); // "0.30000000000000004441"
var_dump(array_sum(array_fill(0, 10, 0.1)) == 1.0); // bool(false)
// Integer cents: exact
$fineCents = 3 * 25 + 199;
printf("%d.%02d EUR\n", intdiv($fineCents, 100), $fineCents % 100); // 2.74 EUR
Source: Floating point numbers
Arbitrary precision: BCMath and BcMath\Number
The BCMath extension computes with decimal numbers represented as strings, with an explicit scale (number of
decimal digits). Functions include bcadd, bcsub, bcmul, bcdiv, bcmod, bcpow, bcsqrt, bccomp and, since
8.4, bcround, bcfloor, bcceil and bcdivmod.
PHP 8.4 added the BcMath\Number class: an immutable decimal object that supports the arithmetic and comparison
operators directly, keeps track of the scale, and converts to string. It is the most convenient way to handle money
or any exact decimal arithmetic in pure PHP.
<?php
declare(strict_types=1);
use BcMath\Number;
var_dump(bcadd('0.1', '0.2', 2)); // string(4) "0.30" -- exact
var_dump(bcdiv('10', '3', 4)); // string(6) "3.3333"
var_dump(bcpow('2', '64')); // string(20) "18446744073709551616"
var_dump(bcround('1.955', 2)); // string(4) "1.96" (8.4)
$price = new Number('19.99');
$total = $price * 3 + new Number('0.03'); // operators work on Number objects (8.4)
var_dump((string) $total, $total->scale); // string(5) "60.00" int(2)
var_dump($total == new Number('60')); // bool(true): compares values
var_dump((string) $total->round(0, RoundingMode::HalfEven)); // string(2) "60"
Division with Number chooses a scale large enough for the result (up to a limit), so call round() explicitly
before presenting or storing a value. Libraries such as brick/money and moneyphp/money add currencies and
allocation on top.
Source: BCMath, BcMath\Number and RFC: Support object type in BCMath
Big integers: GMP
GMP (GNU Multiple Precision) handles arbitrarily large integers very fast. GMP objects support arithmetic and
comparison operators, and functions cover number theory: modular arithmetic, primality tests, factorials, GCD, bit
operations and base conversion.
<?php
declare(strict_types=1);
$n = gmp_init('123456789012345678901234567890');
var_dump(gmp_strval($n * 2)); // string(30) "246913578024691357802469135780"
var_dump(gmp_strval(gmp_mod($n, 97))); // string(2) "52"
var_dump(gmp_strval(gmp_fact(25))); // string(26) "15511210043330985984000000"
var_dump(gmp_prob_prime(104729)); // int(2): definitely prime
var_dump(PHP_INT_MAX + 1); // float(9.223372036854776E+18): native ints overflow to float
Use GMP for integers beyond 64 bits (identifiers, cryptographic values, combinatorics) and BCMath for decimals.
Source: GMP
Randomness
PHP has three generations of random number APIs:
| API | Use |
|---|---|
|
cryptographically secure: tokens, passwords, keys, shuffling for security |
the Random extension, |
object-oriented, chooses an engine: secure by default, or seeded and reproducible |
|
legacy, not secure; |
Random\Randomizer wraps an engine:
-
Random\Engine\Secure(the default when none is passed) — the operating system’s CSPRNG, likerandom_bytes(); -
Random\Engine\Xoshiro256StarStarandRandom\Engine\PcgOneseq128XslRr64— fast, seedable, for reproducible simulations and tests; -
Random\Engine\Mt19937— Mersenne Twister, compatible withmt_rand().
Its methods include getInt(), nextInt(), getFloat() with an IntervalBoundary (8.3), nextFloat(),
getBytes(), getBytesFromString() (8.3, random strings from an alphabet), shuffleArray(), shuffleBytes() and
pickArrayKeys().
<?php
declare(strict_types=1);
use Random\Engine\Xoshiro256StarStar;
use Random\IntervalBoundary;
use Random\Randomizer;
// Secure: a library-card activation code from an unambiguous alphabet
$secure = new Randomizer(); // Secure engine
$alphabet = implode('', array_diff([...range('A', 'Z'), ...range('2', '9')], ['I', 'O'])); // no I/1, O/0
$code = $secure->getBytesFromString($alphabet, 8);
echo strlen($code), ' chars: ', ctype_alnum($code) ? 'ok' : 'bad', "\n";
$token = bin2hex(random_bytes(16)); // 32 hex chars, for a reset link
echo strlen($token), "\n";
// Reproducible: the same seed always gives the same "random" sample (tests, simulations)
$seeded = new Randomizer(new Xoshiro256StarStar(42));
$again = new Randomizer(new Xoshiro256StarStar(42));
var_dump($seeded->getInt(1, 100) === $again->getInt(1, 100)); // bool(true)
$float = $secure->getFloat(0, 1, IntervalBoundary::ClosedOpen); // [0, 1)
var_dump($float >= 0 && $float < 1); // bool(true)
var_dump(count($secure->pickArrayKeys(['Solaris' => 1, 'Kindred' => 1, 'Dawn' => 1], 2))); // int(2): two random keys
Never use rand(), mt_rand(), uniqid() or md5(microtime()) for anything security-related: they are predictable.
Password hashing and token handling are covered in
Cryptography, Hashing and Passwords.
Where older books and tutorials are outdated
-
rand()/mt_rand()for tokens andmd5(uniqid(rand()))(Kromann ch. 14, Wiley ch. 8) are insecure; userandom_bytes()/random_int()or aRandomizerwith the secure engine. -
round()with integerPHP_ROUND_*constants still works, but theRoundingModeenum (8.4) is preferred and adds modes such asTowardsZero. -
BCMath only with functions:
BcMath\Numberand its operators (8.4),bcround()/bcfloor()/bcceil()(8.4) are new. -
fpow()(8.4),clamp()(8.6) and the Random extension (8.2) are absent from every book. -
lcg_value()is deprecated since 8.4.