Numbers & Math

PHP · Lesson 8 of 50 7 min read Updated

Shopping carts, marks, GST, EMI calculators, cricket statistics: almost every program does some maths. PHP has two number types and a good set of maths functions, plus a few traps that every developer should know about.

In this lesson you will learn how PHP stores numbers, how its arithmetic works, how to round and format results, and how to handle money without losing paise.

Integers and floats

PHP has two number types:

  • An int (integer) is a whole number, such as 47, -3 or 0. On a 64-bit system it can go up to PHP_INT_MAX, which is 9,223,372,036,854,775,807.
  • A float (floating-point number) has a decimal point, such as 3.5 or 0.18.

Long numbers are hard to read, so PHP lets you put underscores between digits. PHP ignores them, which means you can even group digits the Indian way:

PHP
<?php
declare(strict_types=1);

$annualFee = 1_25_000;
$population = 3_12_00_000;
$gstRate = 0.18;

var_dump($annualFee);
var_dump($population);
var_dump($annualFee * $gstRate);
Output
int(125000)
int(31200000)
float(22500)

When you multiply an int by a float, the result is always a float, even if it has no decimal part. That is why the GST amount shows as float(22500).

Arithmetic operators

OperatorMeaningExampleResult
+Add7 + 29
-Subtract7 - 25
*Multiply7 * 214
/Divide7 / 23.5
%Remainder (modulo)7 % 21
**Power7 ** 249

Division / gives an int only when the answer is a whole number, otherwise a float. When you want only the whole-number part, use intdiv(). The % operator gives the remainder:

PHP
<?php
declare(strict_types=1);

$students = 47;
$seatsPerBus = 12;

echo $students / $seatsPerBus, "\n";
echo intdiv($students, $seatsPerBus), " full buses\n";
echo $students % $seatsPerBus, " students left over\n";
echo ceil($students / $seatsPerBus), " buses needed\n";
Output
3.9166666666667
3 full buses
11 students left over
4 buses needed

The usual order of operations applies: ** first, then *, / and %, then + and -. Use brackets whenever the order is not obvious, as in ($a + $b) * $c.

Dividing by zero throws a DivisionByZeroError with the message "Division by zero" and stops the script. Check the divisor first when it comes from user input.

Rounding and maths functions

FunctionWhat it doesExampleResult
round($n, $digits)Round to nearestround(1299.456, 2)1299.46
floor($n)Round downfloor(4.9)4
ceil($n)Round upceil(4.1)5
abs($n)Remove the minus signabs(-250)250
max(...), min(...)Largest or smallestmax(45, 78, 62)78
sqrt($n)Square rootsqrt(144)12
pi()The value of pipi()3.1415926535898

round(), floor() and ceil() always return a float, even when the result is a whole number. Cast with (int) if you need an int. By default, round() rounds halves away from zero, so round(2.5) is 3 and round(-2.5) is -3.

PHP
<?php
declare(strict_types=1);

$marks = [78, 91, 66, 84];
$average = array_sum($marks) / count($marks);

echo "Average: ", round($average, 1), "\n";
echo "Highest: ", max($marks), "\n";
echo "Lowest: ", min($marks), "\n";

$radius = 7;
echo "Area of the pitch circle: ", round(pi() * $radius ** 2, 2), "\n";
Output
Average: 79.8
Highest: 91
Lowest: 66
Area of the pitch circle: 153.94

array_sum() and count() add up and count the items in an array. You will learn more array functions in Array Functions.

Floats are not exact

Computers store floats in binary, and many decimal fractions, such as 0.1, cannot be stored exactly. Small errors appear:

PHP
<?php
declare(strict_types=1);

$total = 0.1 + 0.2;

var_dump($total);
var_dump($total == 0.3);
echo $total, "\n";
Output
float(0.30000000000000004)
bool(false)
0.3

echo rounds the number for display, so it looks like 0.3, but the stored value is slightly more. That is why the comparison with 0.3 is false.

For money, this matters. The safe approach is to store amounts as whole paise in ints, do all the maths with ints, and divide by 100 only when you display the amount. For exact decimal maths you can also use the bcmath extension; PHP 8.4 added a BcMath\Number object that makes it easier to use.

Formatting numbers for people

number_format() adds thousands separators and a fixed number of decimal places. It returns a string, ready to print:

PHP
<?php
declare(strict_types=1);

$pricePaise = 129950;
$quantity = 3;
$totalPaise = $pricePaise * $quantity;

echo '₹' . number_format($totalPaise / 100, 2), "\n";
echo number_format(1234567.5, 2), "\n";

$indian = new NumberFormatter('en_IN', NumberFormatter::CURRENCY);
echo $indian->formatCurrency(1234567.5, 'INR'), "\n";
Output
₹3,898.50
1,234,567.50
₹12,34,567.50

number_format() always groups digits in threes (the international style). For the Indian lakh and crore style, use the NumberFormatter class with the en_IN locale, as in the last line. NumberFormatter comes from the intl extension; in XAMPP, enable it by removing the ; before extension=intl in php.ini and restarting Apache.

Random numbers

random_int($min, $max) returns a random whole number between the two values, including both ends. It is cryptographically secure, so it is safe for OTPs and tokens:

PHP
<?php
declare(strict_types=1);

$dice = random_int(1, 6);
$otp = random_int(100000, 999999);

echo "You rolled $dice\n";
echo "Your OTP is $otp\n";

Each run prints different numbers, for example "You rolled 4" and a six-digit OTP. You may see rand() or mt_rand() in older code. They are fine for games, but they are predictable, so never use them for anything related to security.

Common mistakes

  • Storing money as floats. Rounding errors add up. Store paise as ints, or use bcmath.
  • Comparing floats with ==. Compare rounded values instead, for example round($a, 2) === round($b, 2).
  • Expecting / to give an int. 7 / 2 is 3.5. Use intdiv() for whole-number division.
  • Forgetting that round() returns a float. round(4.6) is float(5). Cast to (int) if you need an int.
  • Using rand() for OTPs. Use random_int() for anything secret.

Key points

  • PHP has two number types: int for whole numbers and float for decimals.
  • Underscores in number literals, such as 1_25_000, make big numbers readable.
  • / may return a float; intdiv() gives the whole part and % gives the remainder.
  • round(), floor() and ceil() return floats; max(), min() and abs() work as expected.
  • Floats are not exact, so keep money in paise as ints.
  • number_format() formats for display; NumberFormatter with en_IN gives lakh and crore grouping.
  • Use random_int() for secure random numbers.

Exercise

Write an EMI-style calculator for a ₹49,999 phone bought in 6 monthly instalments. Store the price in paise as an int. Work out the instalment in paise with intdiv(), and the remaining paise that do not divide evenly with %. Print the instalment amount and the leftover amount, both formatted as rupees with two decimal places.

Show solution
PHP
<?php
declare(strict_types=1);

$pricePaise = 49_999_00;
$months = 6;

$instalment = intdiv($pricePaise, $months);
$leftover = $pricePaise % $months;

echo 'Monthly instalment: ₹' . number_format($instalment / 100, 2), "\n";
echo 'Added to the last month: ₹' . number_format($leftover / 100, 2), "\n";
Output
Monthly instalment: ₹8,333.16
Added to the last month: ₹0.04

Working in paise keeps every value an exact int. The few leftover paise are added to one instalment, so the customer pays exactly ₹49,999.00 in total.

Quick Quiz

1. What does 7 / 2 return in PHP?

2. What is 17 % 5?

3. Why should prices be stored as whole paise instead of rupee floats?

4. Which function should you use to generate a login OTP?

Learn PHP with a mentor

Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call