PHP 8.1: Implicit incompatible float to int conversion is deprecated

Version8.1
TypeDeprecation

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 int value: When a float value is cast to an int value, it loses the fractional part of the number, regardless of the value.
    $int_value = (int) 2.87;
    // $int_value = 2; 

    Notice how the .87 is lost when 2.87 is cast to an int. This behavior is different from the floor function, that always rounds down to the nearest integer.

  • Round to the nearest int: Using the round() function, it is possible to round a float value to the nearest integer. It rounds a given float to a given precision, and returns a float value. Setting precision to 0 (parameter default value).
    $float_value = 2.87;
    $int_value = (int) round($float_value);
    // $int_value = 3

    round function returns a float value (3.0 from the example), which is then cast to an int value 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 float keys. 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.


RFC Discussion Implementation