# RFC: Deprecate implicit non-integer-compatible float to int conversions

- Status: Implemented (PHP 8.1)
- Authors: George Peter Banyard, girgias@php.net
- Date: 2021-02-03
- Version: 0.2
- Original: https://wiki.php.net/rfc/implicit-float-int-deprecate
- Page: https://externals.io/rfc/implicit-float-int-deprecate
- More for agents: search with https://externals.io/search.md?q={query} · RFCs at https://externals.io/rfc.md · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

## Votes

### Accept Deprecate implicit non-integer-compatible float to int conversions RFC proposal (closed)

Yes: 29 · No: 0

Voters: alcaeus (Yes), asgrim (Yes), ashnazg (Yes), crell (Yes), daverandom (Yes), derick (Yes), galvao (Yes), girgias (Yes), ilutov (Yes), kalle (Yes), kguest (Yes), kocsismate (Yes), lcobucci (Yes), marandall (Yes), mariano (Yes), nikic (Yes), ocramius (Yes), patrickallaert (Yes), pierrick (Yes), pollita (Yes), reywob (Yes), santiagolizardo (Yes), sergey (Yes), svpernova09 (Yes), tandre (Yes), theodorejb (Yes), thorstenr (Yes), trowski (Yes), twosee (Yes)

## Discussions on the mailing list

- [\[RFC\] Warning for implicit float to int conversions](https://externals.io/message/113077.md) — started on 2021-02-04, 16 messages
- [\[RFC\] Deprecate implicit non-integer-compatible float to int conversions](https://externals.io/message/113371.md) — started on 2021-03-04, 4 messages
- [\[VOTE\] Deprecate implicit non-integer-compatible float to int conversions](https://externals.io/message/113902.md) — started on 2021-04-01, 4 messages

---

## Introduction

PHP is a dynamically typed language and as such implicit type coercion naturally arises, most of these are harmless and rather convenient. However, `float` to `int` conversions can lead to data loss such as when the `float` is outside the platform integer range, or it has a fractional part. This extends to the conversion of float strings when converted to `int`.

## Terminology

A float is said to be integer-compatible if posses the following characteristics:

- Is a number (i.e. not NaN or Infinity)
- Is in range of a PHP integer (platform dependent)
- Has no fractional part

## Proposal

Emit an `E_DEPRECATED` deprecation diagnostic for implicit conversion from `float` and float strings to `int` if the floating point number is not integer-compatible.

If the conversion happens from a `float` the diagnostic message is:

> Implicit conversion to int from non-compatible float %f

If the conversion happens from a `float`-string the diagnostic message is:

> Implicit conversion to int from non-compatible float-string %s

Where `%f` and `%s` correspond to the value of the float/float-string value which is not integer-compatible.

Raise these deprecation diagnostics to `TypeError` in the next major version (PHP 9.0).

## Rationale

Following the changes in behaviour and definition of numeric strings in PHP 8 [1)](#fn__1) [2)](#fn__2) it should be a reasonable expectation to safely be able to use PHP's type juggling from string to integer as such data can come from a variety of places (HTTP request, database query, text file, etc. ). However, because `float`s, and by extension float strings, get silently converted to `int` there is no way to know if the data provided is erroneous and/or provokes data loss.

The lack of possibility of knowing if data loss arises necessitates the use of the `strict_type` mode, which is an issue in itself when using a function which returns a `float` but given the input arguments an `int` compatible return is to be expected, this mostly affects mathematical functions, the most notable example being the `round()` function when passing a non-positive precision.

The use of a `float` or float string as a string offset already emits a warning as it needs to perform a conversion, this proposal would generalize this aspect to other areas of PHP.

Finally, attempting to pass a `float` or float string which exceeds the range representable by an `int` as an argument to a parameter with an `int` type declaration already causes a `TypeError` to be thrown, regardless of typing mode.

## Implementation notes

A new C function `is_long_compatible()` is introduced which performs a round trip (i.e. `(double)(zend_long)`) check to establish if a float is integer-compatible.

A new `zend_dval_to_lval_safe()` C function is introduced which performs a check to `is_long_compatible()` that if it fails will emit the deprecation diagnostic.

The `zend_get_long_func()` is modified to accept an additional argument named `is_lax`, which purpose is to toggle between using `zend_dval_to_lval()` or `zend_dval_to_lval_safe()`, as this function is used by the `(int)` cast.

## Backward Incompatible Changes

The following operations will now emit an `E_DEPRECATED` if a non-integer-compatible `float`s or float string is used:

- Bitwise OR operator `|`
- Bitwise AND operator `&`
- Bitwise XOR operator `^`
- Shift right and left operators
- Modulo operator
- The combined assignment operators of the above operators
- Assignment to a typed property of type `int` in coercive typing mode
- Argument for a parameter of type `int` for both internal and userland functions in coercive typing mode
- Returning such a value for userland functions declared with a return type of `int` in coercive typing mode

The following operations will now emit an `E_DEPRECATED` if a non-integer-compatible `float` is used:

- Bitwise NOT operator `~`
- As an array key

## Proposed PHP Version

Next minor version: PHP 8.1.

## RFC Impact

### To Existing Extensions

Test output might need to be adjusted as the `zval_get_long()` (and TBD `convert_to_long()`) will call the new `zend_dval_to_lval_safe()` function instead of `zend_dval_to_lval()`

Extensions which call directly `zend_get_long_func()` will need to be adjusted to either use `zval_get_long()` or pass explicitly the new `lax` flag.

### To OPcache

Rules about accepting floats instead of int would need to be reviewed as emitting diagnostics pose an issue from my understanding

## Unaffected PHP Functionality

- Manually casting to integer will not raise a notice.
- Integer to float implicit conversions are not affected.
- Strict Type behaviour is unaffected.
- The behaviour of passing float strings as an array key.
- The behaviour of passing a `float` or float string as a string offset.
- A bitwise NOT `~` operation on a float string is still performed with string semantics.

## Future Scope

- Possibility to normalize float strings for array keys.
- Possibility to allow compatible float and float strings for string offsets.
- Normalize behaviour when casting to int from out-of-range float.

## Proposed Voting Choices

As per the voting RFC a yes/no vote with a 2/3 majority is needed for this proposal to be accepted.

**Vote: Accept Deprecate implicit non-integer-compatible float to int conversions RFC proposal** — Yes: 29 · No: 0 (closed)

## Patches and Tests

Patch: <https://github.com/php/php-src/pull/6661>

## Implementation

Implemented in <https://github.com/php/php-src/commit/b6958bb8476306c2f6ce110782330c41e6a5df3a>

## References

Initial mailing list discussion: &lt;<https://externals.io/message/113077>&gt; 
Revised mailing list discussion: &lt;<https://externals.io/message/113371>&gt;

[1)](#fnt__1)[PHP RFC: Saner numeric strings](https://externals.io/rfc/saner-numeric-strings)

[2)](#fnt__2)[PHP RFC: Saner string to number comparisons](https://externals.io/rfc/string_to_number_comparison)
