PHP Numbers

Ma Mariya Vinciya Updated 17 Oct 2026
11 min read ·Lesson 52 of 248
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 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 User-defined Functions Slicing Strings Function Arguments 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 Error Logging HTTP Headers and Redirects Output Buffering Sending Email PHP cURL Consuming REST APIs PHP Generators AJAX PHP AJAX Database AJAX XML AJAX Live Search AJAX Poll AJAX with JSON Security Introduction Password Hashing SQL Injection Prevention XSS Prevention Garbage Collection Project: Simple Blog PHP What is OOP PHP Classes/Objects The $this Keyword Access Array Items Update Array Items Add Array Items Remove Array Items Sorting Arrays Multidimensional Arrays Array Destructuring Array Spread Operator Search in Arrays Merge Arrays PHP Abstract Classes PHP Interfaces Polymorphism PHP Traits PHP Static Methods PHP Static Properties Late Static Binding Magic Methods Object Cloning MySQL Database MySQL Connect MySQL Create DB MySQL Create Table MySQL Insert Data MySQL Get Last ID MySQL Insert Multiple MySQL Prepared MySQL Select Data MySQL Where Debugging PHP Project: Image Upload Gallery $_SESSION $_ENV PHP RegEx RegEx Patterns Heredoc and Nowdoc RegEx Modifiers preg_match preg_replace preg_split PHP RegEx Functions Nullable Types Mixed and Never Types Nullsafe Operator PHP Attributes PHP Fibers Readonly Classes Property Hooks (PHP 8.4) Asymmetric Visibility (PHP 8.4) Reflection API SPL Data Structures Composer PHP Form Handling PDO Transactions PDO Error Handling PHP XML Parsers PHP SimpleXML Parser PHP SimpleXML - Get PHP XML Expat Parser PHP DOM Parser PHP XMLReader PHP XMLWriter AJAX Intro PHP Class Constants Encryption with Sodium Recursive Functions Variable Functions Anonymous Functions Closures Arrow Functions First-class Callable Syntax PHP Arrays Indexed Arrays Associative Arrays Create Arrays Variables Scope Static Variables Variable Variables PHP Echo / Print PHP printf / sprintf PHP Data Types PHP Booleans PHP NULL PHP Strings String Functions array_map / array_filter / array_reduce Array Functions PHP Superglobals $GLOBALS What Is `$_SERVER` ? $_REQUEST $_POST $_GET $_FILES $_COOKIE PHP Type Juggling MySQL Order By MySQL Delete Data MySQL Update Data MySQL Limit Data MySQL Joins with PHP Pagination with PHP PDO Introduction PDO Connect PDO Prepared Statements PDO Fetch Modes PSR-4 Autoloading Installing Extensions (PECL / PIE) OPcache & Performance PHP-FPM Project: Contact Form with Email Project: Login & Registration System Project: CRUD App with MySQL Project: Shopping Cart with Sessions Project: REST API with PHP DateTime Class Timezones PHP Include Include vs Require PHP File Handling PHP File Open/Read PHP File Create/Write File Delete / Rename / Copy PHP Directories PHP CSV Files
Ask about this post
AI Ask about this post

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

Signed-in readers only.