PHP 8.1: Implicit incompatible float to int conversion is deprecated
Converting a float number to an integer often involves losing the fractional value of the float number. For example, 6.8, a float number, will be 6 when converted to an integer. In dynamically typed programming languages such as PHP, sometimes this conversion is unintended and undesirable.
PHP is steadily improving the rules of its dynamic type coercing rules to be more predictable and intuitive. PHP 8.0 has improvements such as locale-independent float to string conversion and numeric string comparison improvements.
From PHP 8.1, a deprecation notice is emitted when a float value is coerced to an int value implicitly, and lost the fractional value in the process. This deprecation notice is not emitted when a float value is explicitly converted to an integer.
The behavior of Strict Types has not changed. When Strict Typing is enabled, PHP continues to throw a TypeError exception on every implicit type coercion. Further, explicitly casting to integers ((int) $floatNum) or and the floatval functions are not affected.
The deprecation notices are emitted when PHP implicitly coerces a float value to an int value.
function getMyInteger(): int {
return '6.8';
}
In the snippet above, getMyInteger function is declared with an int return type. When a float value such as 6.8 is returned, PHP automatically converts it to an int value when Strict Typing is not enabled.
calling getMyInteger in PHP 8.1 and later results in a PHP deprecation notice.
getMyInteger();
// int(6)
PHP Deprecated: Implicit conversion from float-string "6.8" to int loses precision in ... on line ...
float array keys
Array in PHP must be keyed by string or int values. Invalid key types such as objects, bool, callable, Enums and null are illegal, and results in a TypeError. However, float values are handled differently.
When using a float value as the array key, it is silently coerced to an int value. This is the case even if Strict Types are enabled.
$array = [
1.2 => 'foo',
1.4 => 'bar',
1.8 => 'baz',
];
var_dump($array);
array(1) {
[1]=> string(3) "baz"
}
Notice how the $array is created with float values 1.2, 1.3, and 1.8. Because PHP coerces them silently to int, the final $array only holds the last array key/value.
In PHP 8.1, the snippet above helpfully emits the deprecation notice:
Deprecated: Implicit conversion from float 1.2 to int loses precision in ... on line ...
Deprecated: Implicit conversion from float 1.4 to int loses precision in ... on line ...
Deprecated: Implicit conversion from float 1.8 to int loses precision in ... on line ...
Note that it is possible to use numeric strings as array keys:
$array = [
'1.2' => 'foo',
'1.4' => 'bar',
'1.8' => 'baz',
];
var_dump($array);
array(3) {
["1.2"]=> string(3) "foo"
["1.4"]=> string(3) "bar"
["1.8"]=> string(3) "baz"
}
Float numeric strings
This deprecation applies to numeric float strings as well, when the value must be an int value.
$mask = 16 | "128.4";
PHP Deprecated: Implicit conversion from float-string "128.4" to int loses precision in ... on line ...
In the snippet above, "128.4" is a numeric string that contains a valid float number. PHP still evaluates this bit-wise operation by converting the string to int. The new deprecation notice is emitted here as well because the effective bit-wise operation is equal to:
$mask = 16 | 128
Another example of this deprecation notice is the modulo operator (%).
$modulus = 10 % '3.14'; // int(1)
PHP Deprecated: Implicit conversion from float-string "3.14" to int loses precision in ... code on line ...
Explicit int casting
Note that explicitly casting a float value to an int value does not emit the deprecation notice, even if it also loses the fraction part of the float value.
$yolo_pi = (int) 3.14159265359;
var_dump($yolo_pi);
// int(3)
float values without fraction
If the float value being implicitly coerced does not contain a fractional part, there will be no deprecation notices.
$modulus = 10 % 3.0; // int(1);
Avoiding the deprecation notice
The deprecation notice is meant to prevent unintended lose of the fractional part of float values. Explicitly converting the float numbers to an integer makes the intention visible from the PHP code.
There are two approaches convert a float value to an int value.
- Cast to an
intvalue: When afloatvalue is cast to anintvalue, it loses the fractional part of the number, regardless of the value.$int_value = (int) 2.87; // $int_value = 2;Notice how the
.87is lost when2.87is cast to anint. This behavior is different from thefloorfunction, that always rounds down to the nearest integer. - Round to the nearest
int: Using theround()function, it is possible to round afloatvalue to the nearest integer. It rounds a givenfloatto a given precision, and returns afloatvalue. Setting precision to0(parameter default value).$float_value = 2.87; $int_value = (int) round($float_value); // $int_value = 3roundfunction returns afloatvalue (3.0from the example), which is then cast to anintvalue with the(int)cast.
Backwards Compatibility Impact
Fixes to avoid and correct the deprecation notices should be compatible with older PHP versions as well. The actual rules of float to int conversion is not changed in PHP 8.1, but such conversions will result in a TypeError exception in PHP 9.0 and later.
The following functionality are affected:
- Arrays with
floatkeys. Numeric strings containing floats are allowed. - Parameter types, return types, and property types.
- Bit-wise operators:
|/OR,&/AND, ``/XOR. e.g.:6.8 | 1024; - Modulus operator (
%). e.g.:45 % 5.2; - Bit shift operators:
>>and<<. e.g.:2 << 4.1; 65536 >> 6.8;
When working with dynamic values for array keys, explicitly casting them to a string might be better approach:
public function setCache(string|int|float $key, mixed $value): void {
- $this->cache[$key] = $value;
+ $this->cache[(string) $key] = $value;
}
The snippet above uses Union Types and mixed type introduced in PHP 8.0.