Introduction
Rounding is an extremely mainstream operation when it comes to numbers in any programming language.
Needless to say, rounding is a concern typically associated with floating-point numbers, not with integers. Who wants to round integers anyways?
In this chapter, we aim to explore all the different ways to round floating-point numbers in PHP, particularly using the round()
function with its different modes and the functions floor()
and ceil()
.
We'll also see how to use round()
to round a number to given decimal places, and in some cases, to given significant figures.
The round()
function
PHP provides a goto function to round floating-point numbers to a given precision.
Here's how it looks:
round($number, $precision = 0, $mode = PHP_ROUND_HALF_UP)
The first $number
argument is the number to round, while the second $precision
argument is the precision. This second argument is optional and defaults to 0
. That is, the number is rounded to the nearest unit.
We'll explore the last $mode
argument later below.
$precision
can be a positive integer or a negative integer. Here's how $precision
works on a given number 1780.5389
.
If $precision
is 0
, the number is rounded to the nearest units as shown in the figure.
If it's positive, the corresponding position to the right is determined and the number rounded to that very position.
For instance, $precision
of 1
would round 1780.5389
to 1780.5
while a $precision
of 2
would round 1780.5389
to 1780.54
. And so on and so forth.
What would round(1780.5389, 3)
return?
1780.54
1780.538
1780.539
round(1780.5389, 3)
rounds 1780.5389
to the nearest thousandth place (as highlighted) to give the number 1780.539
. This goes with choice (C).Similarly, a negative value of $precision
would shift this position leftwards.
For instance, a $precision
of -1
would round 1780.5389
to 1780
; while a $precision
of -2
would round 1780.5389
to 1800
.
What would round(1780.5389, -3)
return?
1780.0
1800.0
2000.0
round(1780.5389, -3)
rounds 1780.5389
to the nearest thousands (as highlighted) to give the number 2000.0
. This goes with choice (C).Simple.
Let's use the round()
function on the same number 1780.5389
with different precisions:
<?php
$num = 1780.5389;
echo 'Precision of 5: ', round($num, 5), "\n";
echo 'Precision of 4: ', round($num, 4), "\n";
echo 'Precision of 3: ', round($num, 3), "\n";
echo 'Precision of 2: ', round($num, 2), "\n";
echo 'Precision of 1: ', round($num, 1), "\n";
echo 'Precision of 0: ', round($num, 0), "\n";
echo 'Precision of -1: ', round($num, -1), "\n";
echo 'Precision of -2: ', round($num, -2), "\n";
echo 'Precision of -3: ', round($num, -3), "\n";
echo 'Precision of -4: ', round($num, -4), "\n";
echo 'Precision of -5: ', round($num, -5), "\n";
Notice that beyond a precision of 4
here, there is no difference between the rounded numbers. This is because rounding can only happen upto a precision that's equal to or less than the precision of the given number. In the case above, 1780.5389
is to a precision of 4 decimal places and therefore we can't go more precise than that.
Moreover, when the precision is negative and causes the number to be rounded to a position that's not in the number, 0
is returned. This is what happens in round(1780.5389, -4)
. The very first position in 1780.5389
is represented by a $precision
value of -3
— going less than this value is not possible.
Apart from $precision
, another useful argument of the round()
function is the third $mode
argument.
$mode
argument serves to specify the exact rounding behavior to use on the digit pointed by the $precision
argument when the sequence of digits after it form a half value.For instance, it would be applied on the numbers
1.35
(in round(1.35, 1)
), 1.5
(in round(1.5)
), 2750
(in round(2750, -2)
), where the sequence of digits highlighted after the desired position form a perfect half. However, it would NOT be applied on the numbers 1.352
, 1.5005
, 2751
.The four possible modes are given by the following four constants.
Mode | Purpose |
---|---|
PHP_ROUND_HALF_UP | Round the previous digit to the next digit. |
PHP_ROUND_HALF_DOWN | Round the previous digit to the previous digit. |
PHP_ROUND_HALF_EVEN | Round the previous digit to the next or previous digit, whichever is even. |
PHP_ROUND_HALF_ODD | Round the previous digit to the next or previous digit, whichever is odd. |
$mode
is an optional parameter which, if omitted, defaults to PHP_ROUND_HALF_UP
.
Let's consider a couple of examples.
In the code below, we round the number 1.35
to 1 decimal place (since that's when the next digit 5
would be considered in the rounding phase) in all the aforementioned modes:
<?php
$num = 1.35;
echo 'Round up: ', round($num, 1, PHP_ROUND_HALF_UP), "\n";
echo 'Round down: ', round($num, 1, PHP_ROUND_HALF_DOWN), "\n";
echo 'Round to even: ', round($num, 1, PHP_ROUND_HALF_EVEN), "\n";
echo 'Round to odd: ', round($num, 1, PHP_ROUND_HALF_ODD), "\n";
Similarly, below we round the number 2650
to the nearest hundreds (since that's when the next digit 5
would be considered in the rounding phase) in all the modes:
<?php
$num = 2650;
echo 'Round up: ', round($num, -2, PHP_ROUND_HALF_UP), "\n";
echo 'Round down: ', round($num, -2, PHP_ROUND_HALF_DOWN), "\n";
echo 'Round to even: ', round($num, -2, PHP_ROUND_HALF_EVEN), "\n";
echo 'Round to odd: ', round($num, -2, PHP_ROUND_HALF_ODD), "\n";
Note that, as stated before, if the sequence of digits after the digit given by $precision
is not a perfect half, the mode won't matter.
This can be seen as follows. The code is the same as before except for adding changing the number from 2650
to 2651
:
<?php
$num = 2651;
echo 'Round up: ', round($num, -2, PHP_ROUND_HALF_UP), "\n";
echo 'Round down: ', round($num, -2, PHP_ROUND_HALF_DOWN), "\n";
echo 'Round to even: ', round($num, -2, PHP_ROUND_HALF_EVEN), "\n";
echo 'Round to odd: ', round($num, -2, PHP_ROUND_HALF_ODD), "\n";
As we know, 51
is not a perfect half and hence when PHP tries to round the digit 6
(as given by the $precision
argument -2
), it is rounded upwards to 7
to finally give the number 2700
(since 51
is above the half mark).
Floor and ceil
As you start to learn to implement various algorithms and data structures in PHP, you'll start to see the applications of two very common ideas from number theory — floor and ceil.
Both of these are essentially mathematical functions that round a given number to a particular integer. Let's see what both of them exactly do.
For instance, ::\lfloor -10 \rfloor:: gives ::-10::, ::\lfloor -3.2 \rfloor:: gives ::-4::, ::\lfloor 5.02 \rfloor:: gives ::5::, ::\lfloor 15.99 \rfloor:: gives ::15::, ::\lfloor 10 \rfloor:: gives ::10::, and so on.
In contrary to this:
For instance, ::\lceil -10 \rceil:: gives ::-10::, ::\lceil -3.2 \rceil:: gives ::-3::, ::\lceil 5.02 \rceil:: gives ::6::, ::\lceil 15.99 \rceil:: gives ::16::, ::\lceil 10 \rceil:: gives ::10::, and so on.
In PHP, the functions floor()
and ceil()
allow us to perform these very mathematical operations on given numbers.
Consider the code below where we use the floor()
function on the same values as shown above:
<?php
echo floor(-10), "\n";
echo floor(-3.2), "\n";
echo floor(5.02), "\n";
echo floor(15.99), "\n";
echo floor(10), "\n";
Now, time for the ceil()
function:
<?php
echo ceil(-10), "\n";
echo ceil(-3.2), "\n";
echo ceil(5.02), "\n";
echo ceil(15.99), "\n";
echo ceil(10), "\n";
Easy, wasn't this?
floor()
and ceil()
in PHP return floats!
Note that contrary to what one might expect, the floor()
and ceil()
functions both return back a float, NOT an integer!
<?php
var_dump(floor(10.5));
var_dump(ceil(10.5));
This is extremely important to keep in mind.
Many languages provide similar floor()
and ceil()
functions but they return back integers. PHP, however, takes a different approach here.
But why?
Well, as per the official documentation, this is done to enable representing values that otherwise couldn't be represented in the native int
type.
This approach only benefits 32-bit machines. On 32-bit machines, the maximum integer possible in the native int
type is 2147483647
. However, the maximum integer possible in the float
type is 9007199254740991
.
64-bit machines, on the other hand, don't really benefit from this approach. This is because the maximum value of the int
type on a 64-bit machine is larger than the maximum integer capable to be represented in the float
format.
However, this isn't a bad approach even from the perspective of a 64-bit machine.
This is because floats larger than the maximum int
value can't really be precise integers and hence, in any way, there would be no advantage of making the return type of floor()
or ceil()
an integer.