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).