PHP Numbers

Ma Mariya Vinciya Updated 17 Oct 2026
11 min read ·Lesson 53 of 102
In this post

PHP has two numeric types: Type Holds Examples int Whole numbers, positive or negative 0 , 42 , -17 , 1_000_000 float Numbers with a fractional part or in exponential notation 3.14 , -0.5 , 1.5e3 This

PHP has two numeric types:

TypeHoldsExamples
intWhole numbers, positive or negative0, 42, -17, 1_000_000
floatNumbers with a fractional part or in exponential notation3.14, -0.5, 1.5e3

This lesson focuses on integers: how to write them, their limits, how arithmetic behaves, how to convert and validate numeric input, and what to do when numbers become too large. Floats have their own lesson (PHP Floats) because they behave differently in important ways.

Why Integers Deserve Careful Attention

Integers appear everywhere: quantities, IDs, prices stored in the smallest currency unit, page numbers, ages, timestamps, and counters. Most numeric bugs come from three sources: input that is not actually an integer, division that produces a float unexpectedly, and numbers that exceed the integer range.


Writing Integer Literals

Example

<?php
$decimal = 255;
$hex = 0xFF;            // hexadecimal (base 16)
$octal = 0o377;         // octal (base 8), explicit prefix — PHP 8.1+
$legacyOctal = 0377;    // octal, legacy form: leading zero
$binary = 0b11111111;   // binary (base 2)
$million = 1_000_000;   // underscores for readability — PHP 7.4+

var_dump($decimal, $hex, $octal, $legacyOctal, $binary, $million);

Output

int(255)
int(255)
int(255)
int(255)
int(255)
int(1000000)

How It Works

  • All five notations produce the same integer. The notation only affects how the number is written in source code, not how it is stored.
  • Hexadecimal (0x) is common for colors, byte values, and bit flags.
  • Octal is mainly used for Unix file permissions, such as chmod($file, 0o644). The 0o prefix (PHP 8.1+) is clearer than the legacy leading-zero form.
  • Binary (0b) is useful when working with individual bits.
  • Underscores are ignored by PHP and make large numbers readable: 1_000_000 is easier to check than 1000000.

Warning: A leading zero changes the meaning of a number literal. $code = 0123; is octal, equal to 83, not one hundred twenty-three. Never write decimal numbers with leading zeros in code. (This rule applies to literals in source code only; the string "0123" from a form converts to 123.)


Integer Size and Limits

On 64-bit systems (all modern servers), integers are stored in 8 bytes.

<?php
echo PHP_INT_MAX . "\n";
echo PHP_INT_MIN . "\n";
echo PHP_INT_SIZE . " bytes\n";

Output

9223372036854775807
-9223372036854775808
8 bytes

That is about ±9.2 quintillion — enough for IDs, timestamps, and most money amounts stored in paise or cents.

Integer Overflow

When a calculation exceeds PHP_INT_MAX, PHP does not wrap around or throw an error. The result silently becomes a float:

<?php
$max = PHP_INT_MAX;

var_dump($max);
var_dump($max + 1);

$counter = PHP_INT_MAX;
$counter++;
var_dump($counter);

Output

int(9223372036854775807)
float(9.2233720368547758E+18)
float(9.2233720368547758E+18)

Why This Matters

Floats have only about 15–17 significant digits of precision. Once a value becomes a float, its last digits can no longer be represented exactly. For example, the result shown is 9223372036854775808 — but PHP_INT_MAX + 2 would produce the same float, because the gap between neighboring floats at this size is larger than 1. Calculations near the limit silently lose precision. For numbers this large, use BCMath or GMP (covered below).


Arithmetic With Integers

Example

<?php
var_dump(10 + 5);
var_dump(10 - 15);
var_dump(6 * 7);
var_dump(10 / 2);
var_dump(7 / 2);
var_dump(intdiv(7, 2));
var_dump(7 % 3);
var_dump(2 ** 10);
var_dump(2 ** -1);

Output

int(15)
int(-5)
int(42)
int(5)
float(3.5)
int(3)
int(1)
int(1024)
float(0.5)

Division Returns a Float When the Result Is Not Whole

The / operator returns an int only when both operands are integers and the division is exact (10 / 2 → int(5)). Otherwise it returns a float (7 / 2 → float(3.5)). Code that expects an integer — such as an array index or a page number — must handle this explicitly.

Integer Division: intdiv()

intdiv($a, $b) divides and discards the fractional part, always returning an int. It truncates toward zero:

<?php
var_dump(intdiv(7, 2));
var_dump(intdiv(-7, 2));

Output

int(3)
int(-3)

Modulo: %

% returns the remainder of integer division. The sign of the result follows the left operand:

<?php
var_dump(7 % 3);
var_dump(-7 % 3);
var_dump(7 % -3);

Output

int(1)
int(-1)
int(1)

Modulo is used for:

  • Checking even/odd: $n % 2 === 0
  • Alternating row styles in tables: $index % 2
  • Wrapping values in a range: ($hour + 5) % 24
  • Running something every N items: if ($i % 100 === 0)

% converts its operands to integers. For the remainder of floats, use fmod().

Exponentiation: **

2 ** 10 raises 2 to the power 10. A negative exponent produces a float (2 ** -1 → 0.5), and results larger than PHP_INT_MAX become floats.

Division by Zero

<?php
try {
    echo 10 / 0;
} catch (DivisionByZeroError $e) {
    echo "Error: " . $e->getMessage() . "\n";
}

try {
    echo 10 % 0;
} catch (DivisionByZeroError $e) {
    echo "Error: " . $e->getMessage() . "\n";
}

Output

Error: Division by zero
Error: Modulo by zero

Since PHP 8.0, dividing by zero with /, %, or intdiv() throws a DivisionByZeroError. In PHP 7, / produced a warning and returned false or INF. Check divisors that come from data (for example, the number of items when calculating an average) before dividing. Exceptions are covered in PHP Exceptions.


Converting Values to Integers

Casting and intval()

<?php
var_dump((int) "42");
var_dump((int) "  42");
var_dump((int) "42abc");
var_dump((int) "abc");
var_dump((int) 9.99);
var_dump((int) -9.99);
var_dump((int) "1e3");
var_dump((int) true);
var_dump((int) null);

Output

int(42)
int(42)
int(42)
int(0)
int(9)
int(-9)
int(1000)
int(1)
int(0)

Conversion Rules

InputResultRule
Numeric stringThe numberLeading whitespace is allowed
Leading-numeric string ("42abc")The leading numberThe rest is ignored
Non-numeric string ("abc")0No error from a cast
FloatTruncated toward zero9.99 → 9, -9.99 → -9 (no rounding)
Numeric string in exponent form ("1e3")1000Since PHP 7.1
true / false1 / 0
null0

Casting never fails: it always returns some integer. That makes it unsuitable for validation — "abc" silently becomes 0. Validate first, then convert.

To round instead of truncating, use (int) round($value).

Other Bases

<?php
var_dump(intval("42", 8));      // interpret as octal
var_dump(intval("0x1A", 16));   // hexadecimal with prefix
var_dump(hexdec("ff"));
var_dump(bindec("1010"));
var_dump(octdec("777"));
var_dump(dechex(255));
var_dump(decbin(10));

Output

int(34)
int(26)
int(255)
int(10)
int(511)
string(2) "ff"
string(4) "1010"

intval() accepts a base as its second argument. hexdec(), bindec(), and octdec() convert strings in those bases to numbers, and dechex(), decbin(), and decoct() convert numbers into strings in those bases.


Validating Integer Input

Form fields, URL parameters, and JSON may contain anything. Choose a validation method that matches the rules you need.

Comparing Validation Functions

<?php
$inputs = ["42", "-42", "4.2", "007", " 42", "42abc", ""];

foreach ($inputs as $input) {
    printf(
        "%-8s is_numeric: %-5s ctype_digit: %-5s FILTER_VALIDATE_INT: %s\n",
        "'$input'",
        var_export(is_numeric($input), true),
        var_export(ctype_digit($input), true),
        var_export(filter_var($input, FILTER_VALIDATE_INT), true)
    );
}

Output

'42'     is_numeric: true  ctype_digit: true  FILTER_VALIDATE_INT: 42
'-42'    is_numeric: true  ctype_digit: false FILTER_VALIDATE_INT: -42
'4.2'    is_numeric: true  ctype_digit: false FILTER_VALIDATE_INT: false
'007'    is_numeric: true  ctype_digit: true  FILTER_VALIDATE_INT: false
' 42'    is_numeric: true  ctype_digit: false FILTER_VALIDATE_INT: 42
'42abc'  is_numeric: false ctype_digit: false FILTER_VALIDATE_INT: false
''       is_numeric: false ctype_digit: false FILTER_VALIDATE_INT: false

What Each Function Checks

FunctionAcceptsNotes
is_numeric()Any int or float format, including "4.2" and "1e3"Too broad for "whole number" checks
ctype_digit()Only the characters 0–9, at least oneRejects negatives and whitespace; accepts leading zeros. Pass strings only (passing an int is deprecated since PHP 8.1)
filter_var(..., FILTER_VALIDATE_INT)Valid integers, optional sign; surrounding whitespace is trimmedRejects leading zeros ("007"), decimals, and out-of-range values; returns the converted int or false

filter_var() is usually the best choice: it validates and converts in one step.

Validating a Range

<?php
$options = ['options' => ['min_range' => 1, 'max_range' => 100]];

var_dump(filter_var("25", FILTER_VALIDATE_INT, $options));
var_dump(filter_var("0", FILTER_VALIDATE_INT, $options));
var_dump(filter_var("150", FILTER_VALIDATE_INT, $options));

Output

int(25)
bool(false)
bool(false)

Setting min_range and max_range enforces business rules — for example, a quantity between 1 and 100 — in the same call. Because a valid result could be 0 in other situations, always check the result with === false.


Integers and JavaScript / JSON

PHP integers are 64-bit, but JavaScript numbers are 64-bit floats that can represent integers exactly only up to 9007199254740991 (Number.MAX_SAFE_INTEGER, which is 2^53 − 1).

<?php
echo json_encode(['id' => 9007199254740993]);

Output

{"id":9007199254740993}

PHP produces the correct JSON, but a browser reading it with JSON.parse() gets 9007199254740992 — a different ID. Large IDs (such as those from distributed ID generators like Snowflake) should be sent to JavaScript as strings:

<?php
echo json_encode(['id' => (string) 9007199254740993]);

Output

{"id":"9007199254740993"}

When decoding JSON with integers larger than PHP_INT_MAX, PHP converts them to floats. The JSON_BIGINT_AS_STRING flag keeps them as strings:

<?php
$json = '{"amount": 99999999999999999999}';

var_dump(json_decode($json, true)['amount']);
var_dump(json_decode($json, true, 512, JSON_BIGINT_AS_STRING)['amount']);

Output

float(1.0E+20)
string(20) "99999999999999999999"

Numbers Beyond the Integer Range: BCMath and GMP

For values larger than PHP_INT_MAX, or when every digit must be exact, PHP provides two extensions that work with numbers stored as strings or objects:

ExtensionBest ForExample Function
BCMathArbitrary-precision decimal arithmetic (money, high-precision calculations)bcadd(), bcmul(), bcdiv()
GMPVery large integers (cryptography, number theory)gmp_add(), gmp_pow()

Example

<?php
echo bcadd('9223372036854775807', '1') . "\n";
echo bcmul('123456789123456789', '987654321') . "\n";
echo gmp_strval(gmp_pow(2, 100)) . "\n";

Output

9223372036854775808
121932631234567900112635269
1267650600228229401496703205376

All digits are exact. These extensions must be installed (php-bcmath, php-gmp on Linux). BCMath's use for decimal money calculations is covered in PHP Floats.


Formatting Integers for Display

<?php
$population = 1234567;

echo number_format($population) . "\n";
echo number_format($population, 0, '', ' ') . "\n";

$formatter = new NumberFormatter('en_IN', NumberFormatter::DECIMAL);
echo $formatter->format($population) . "\n";

$currency = new NumberFormatter('en_IN', NumberFormatter::CURRENCY);
echo $currency->formatCurrency($population, 'INR') . "\n";

Output

1,234,567
1 234 567
12,34,567
₹12,34,567.00
  • number_format() groups digits in thousands with a chosen separator.
  • NumberFormatter (from the intl extension) follows the rules of a locale. For en_IN, digits are grouped in the Indian system (lakhs and crores), and currency formatting adds the rupee symbol and decimals automatically.

Formatting produces strings for display. Keep calculations on the original integers.


Practical Example: Pagination

Pagination combines several integer operations: division, rounding up, clamping a value into a range, and calculating an offset.

<?php
function paginate(int $totalItems, int $perPage, int $requestedPage): array
{
    $totalPages = max(1, (int) ceil($totalItems / $perPage));
    $page = max(1, min($requestedPage, $totalPages));
    $offset = ($page - 1) * $perPage;

    return ['page' => $page, 'totalPages' => $totalPages, 'offset' => $offset];
}

$requested = filter_var($_GET['page'] ?? '1', FILTER_VALIDATE_INT) ?: 1;

foreach ([[47, 10, 3], [47, 10, 99], [47, 10, -2], [0, 10, 1]] as [$total, $perPage, $page]) {
    $result = paginate($total, $perPage, $page);
    printf(
        "items=%d, page %d requested -> page %d of %d, offset %d\n",
        $total, $page, $result['page'], $result['totalPages'], $result['offset']
    );
}

Output

items=47, page 3 requested -> page 3 of 5, offset 20
items=47, page 99 requested -> page 5 of 5, offset 40
items=47, page -2 requested -> page 1 of 5, offset 0
items=0, page 1 requested -> page 1 of 1, offset 0

Step-by-Step Explanation

  1. $totalItems / $perPage gives 4.7 for 47 items; ceil() rounds up to 5.0, because the last partial page still needs a page. (int) converts the float result to an integer.
  2. max(1, ...) guarantees at least one page, even when there are no items.
  3. max(1, min($requestedPage, $totalPages)) clamps the requested page into the valid range, so 99 becomes the last page and -2 becomes the first.
  4. The offset — how many items to skip in a database query (LIMIT 10 OFFSET 20) — is ($page - 1) * $perPage.
  5. The $requested line shows how the page number would be read from the URL: filter_var() returns an int or false, and ?: 1 falls back to page 1 for invalid input (including 0).

Common Mistakes

Leading Zeros in Literals

Effect: $pin = 0123; stores 83 (octal).

Fix: Do not write leading zeros on decimal literals; store codes with leading zeros (PIN codes, phone numbers) as strings.

Expecting / to Return an Integer

Effect: 7 / 2 is 3.5; using the result as an array index triggers a deprecation or wrong behavior.

Fix: Use intdiv() for integer division, or (int) round(...) / ceil() / floor() deliberately.

Validating With Casts

Effect: (int) "abc" becomes 0 and passes as a valid number.

Fix: Validate with filter_var(..., FILTER_VALIDATE_INT) first.

Using is_numeric() to Check for Whole Numbers

Effect: "4.2" and "1e3" pass.

Fix: Use FILTER_VALIDATE_INT or ctype_digit().

Ignoring Overflow

Effect: Large values silently become imprecise floats.

Fix: Use BCMath or GMP for values that can exceed PHP_INT_MAX.

Sending Large IDs to JavaScript as Numbers

Effect: IDs above 2^53 change value in the browser.

Fix: Encode them as strings in JSON.


Best Practices

  • Use underscores in long numeric literals for readability, and 0o for octal.
  • Store numbers with meaningful leading zeros (PIN codes, phone numbers, ZIP codes) as strings — they are identifiers, not quantities.
  • Validate integer input with filter_var() and range options.
  • Use intdiv() when you need integer division, and check divisors before dividing.
  • Store money as integers in the smallest unit (paise, cents) or use BCMath.
  • Use number_format() or NumberFormatter only for display.

Official reference: https://www.php.net/manual/en/language.types.integer.php

0 Comments

Reviewed before they appear

No comments yet.

PHP
PHP Home PHP Switch PHP Match Alternative Syntax PHP Loops While Loop Do While Loop For Loop Foreach Loop Nested Loops Break Statement PHP Home PHP Intro PHP Install PHP Built-in Web Server PHP Syntax PHP Tags PHP and HTML PHP Comments PHP Multiline Comments PHP Variables Variables Scope PHP Casting Logical Operators Null Coalescing Operator Spaceship Operator Operator Precedence Continue Statement Modify Strings PHP If...Else...Elseif PHP If Modify Strings PHP If Operators PHP Functions Concatenate Strings PHP If...Else Slicing Strings User-defined Functions Slicing Strings PHP Shorthand if Escape Characters Default Argument Values PHP Nested if Pass by Reference String Interpolation Named Arguments PHP File Upload Variadic Functions Return Values Search in Strings Multibyte Strings Type Declarations in Functions PHP Numbers PHP Math PHP Floats PHP Random Numbers PHP Constants PHP Properties PHP Methods PHP Constructor Constructor Property Promotion PHP Cookies PHP Destructor PHP Access Modifiers Readonly Properties PHP Sessions PHP Inheritance Method Overriding PHP Filters The final Keyword CSRF Protection PHP Filters Advanced Session Security Secure Cookies PHP Magic Constants File Upload Security PHP Callback Functions PHP Operators Filesystem Security PHP JSON Arithmetic Operators Error Reporting in Production Hiding PHP Assignment Operators PHP Configuration (php.ini) Comparison Operators PHP Command Line (CLI) PHP Exceptions Increment / Decrement Operators Migrating to PHP 8 PHP Form Validation PHP Form Required PHP Form URL/E-mail Error Handling Form Input Sanitizing Escaping Output (htmlspecialchars) Custom Error Handler Checkbox, Radio and Select Inputs Sticky Forms PHP Form Complete PHP Date and Time Date Formatting
Ask about this post
AI Ask about this post

Ask questions about PHP Numbers and get answers drawn from it.

Signed-in readers only.