Mathematics
Casting INF
and
NAN
to Integer
The concept of infinity is used in mathematics to describe
something that has no bounds or is larger than any other number.
Positive and negative infinity values can be the result of
arithmetic overflow or division by zero, for instance. In PHP,
infinity is represented as a constant, INF
, of type
float
:
var_dump(1 / 0);
Executing the code shown above prints the output shown below:
Warning: Division by zero in ...
float(INF)
Before PHP 7, casting INF
to integer resulted in the
smallest integer supported in the build of PHP that is used:
var_dump((int) INF);
Executing the code shown above using PHP 5.6 prints the output shown below:
int(-9223372036854775808)
Executing the same code using PHP 7 prints the output shown below:
int(0)
NaN, short for “not a number”, is a special value that is used to
represent an undefined or unrepresentable value. In PHP, NaN is
represented as a constant, NAN
, of type
float
:
$nan = 0 / 0;
var_dump($nan);
Executing the code shown above will print the output shown below:
Warning: Division by zero in ...
float(NAN)
Before PHP 7, casting NAN
to integer resulted in the
smallest integer supported in the build of PHP that is used:
var_dump((int) NAN);
Executing the code shown above using PHP 5.6 prints the output shown below:
int(-9223372036854775808)
Executing the same code using PHP 7 prints the output shown below:
int(0)
Division by Zero
PHP 5 emitted an E_WARNING
and returned
false
when the divisor of a division was zero:
var_dump(1 / 0);
Executing the code shown above with PHP 5 yielded the output shown below:
Warning: Division by zero in ...
bool(false)
PHP 5 behaved the same when the divisor of a modulo operation was zero:
var_dump(0 % 0);
Executing the code shown above with PHP 5 yielded the output shown below:
Warning: Division by zero in ...
bool(false)
PHP 7 returns float(INF)
instead of
bool(false)
but still emits an E_WARNING
when the divisor of a division operation is zero:
Warning: Division by zero in ...
float(INF)
PHP 7 raises an DivisionByZeroError
when the divisor
of a modulo operation is zero:
Fatal error: Uncaught DivisionByZeroError:
Modulo by zero in ...
Architecture-Independent Bit Shifting
Even though they are not commonly used in day to day web applications, bitwise operations are a very powerful tool. Previous versions of PHP allowed shift operations that are logically invalid. This has been corrected for PHP 7.
Bitwise shifts by a negative number will now throw an ArithmeticError:
var_dump(1 >> -1);
While PHP 5.6 silently accepted it and outputs “int(0)”, when run with PHP 7 the above code will trigger an ArithmeticError:
PHP Fatal error: Uncaught ArithmeticError:
Bit shift by negative number in ...
When shifting beyond the bit width of the integer value, the
result was dependent on the used CPU architecture. For instance
running the following example on an x86 or x86_64 CPU would output
1
:
var_dump(1 << 64);
This has been corrected to always, independently of the CPU
architecture to return the value 0
. Similarly right
bitwise shifts beyond the bit width will now always result in 0 or
-1 (depending on sign):
var_dump(1 >> 64);
var_dump(-1 >> 64);
Negative
Index for gmp_setbit()
and gmp_clrbit()
Return false
The gmp
extension (GNU Multiple Precision) allows
for arbitrary-length integers to be worked with in PHP. Among the
many functions this extension provides, two allow the direct setting
and unsetting of bits: gmp_setbit()
and
gmp_clrbit()
.
Both functions require a GMP resource object and the index of the bit to flip. As should be obvious, that index position must not be negative:
gmp_setbit(gmp_init(2), -1);
PHP Warning:
gmp_setbit(): Index must be greater than or equal to zero in ...
For PHP 7, the behavior got slightly adjusted. While the warning
is still present, both gmp_setbit()
and
gmp_clrbit()
now return false
instead of
NULL
in case the specified index is negative.
More Precise Float Value Handling
When you want to confuse people, try this:
var_dump((int) (16.24 * 100));
You would expect the result to be 1624
, right? Well,
prepare for a little surprise:
(int)1623
No, this is not a bug in PHP, other programming languages will
expose exactly the same behavior. You are looking at a rounding
issue that exists due to the nature of how computers represent
floating point numbers. In short: some floating point numbers like
1/3
have an infinite number of digits, but a computer’s
memory is finite. So there have to be precision issues. Since a
computer uses 64 bits to represent floating point numbers, those are
far more common than you may think.
By the way, you can (and should) “fix” the above example by rounding:
var_dump((int) round(16.24 * 100));
PHP has always had an ini setting precision
that
controls how many digits of a floating point number are “exported”,
for example when serializing a value or displayed it with
var_dump()
. The precision
setting default
to 14. Let us change this value to see how imprecise floating point
values really are:
ini_set('precision', 25);
var_dump(1/3);
Running this program, we get the following output:
double(0.3333333333333333148296163)
Things look okay first, but start to be a little off on the long run.
Another similar php.ini
setting is
serialize_precision
, which exists since PHP 4.3.2.
Initially, it had a default value of 100
, which then
was changed to 17
. Up to PHP 7,
json_encode()
uses precision
rather than
serialize_precision
In PHP 7.1 this has changed. In
addition, both settings can be set to -1
in PHP 7.1,
which activates a better rounding algorithm that minimizes precision
loss when serializing floating point numbers.
Please note that this change could cause issues when passing back and forth values between different systems that assume that they get exact numbers. After all, floating point numbers are not exact.
bcmod()
and
Fractional Numbers
PHP provides two functions, bcmod()
and
fmod()
, as well as the %
operator for
performing the modulo operation to find the remainder after division
of one number by another. Provided by the bcmath
extension for arbitrary precision mathematics, bcmod()
operates on string
arguments that represent numbers
whereas fmod()
operates on float
arguments. Finally, the %
operator converts its
operands to int
by stripping their decimal part, if
they have any.
Prior to PHP 7.2, the bcmod()
function behaved like
the %
operator and truncated its parameters to
integer:
bcscale(2);
var_dump(bcmod('4', '3.5'));
var_dump(fmod('4', '3.5'));
var_dump(4 % 3.5);
Executing the code shown above with PHP 7.1 prints the following output:
string(1) "1"
float(0.5)
int(1)
Executing the same code with PHP 7.2 prints the following output:
string(4) "0.50"
float(0.5)
int(1)
As of PHP 7.2, the bcmod()
function is now
consistent with the behavior of the fmod()
function
instead of with the %
operator.
Changes to Random Number Generation
With PHP 7.1, rand()
and srand()
have
been aliased to mt_rand()
and mt_srand()
,
respectively. Consequently, the output and behaviour of the PHP
functions rand()
, shuffle()
,
str_shuffle()
, and array_rand()
has
slightly changed.
Due to internal changes of the way how pseudo-random numbers are
being generated, functions such as mt_rand()
produce a
different random number sequence in PHP 7.2 than previous versions
of PHP. If your code relies on functions such as
mt_srand()
to produce a deterministic sequence, the old
sequence can still be produced by specifying
MT_RAND_PHP
as the second parameter:
mt_srand($seed, MT_RAND_PHP)
.