====== PHP RFC: Add "is_representable_as_float()" and "is_representable_as_int()" functions ======
* Version: 1.0
* Date: 2025-07-29
* Author: Alexandre Daubois, alex.daubois@gmail.com
* Status: Under Discussion
* Implementation: https://github.com/php/php-src/pull/19308
===== Introduction =====
This RFC proposes the addition of two new functions to PHP's standard library: is_representable_as_float() and
is_representable_as_int().
These functions provide developers with a standardized way to check whether values can be losslessly
converted between integer and floating-point representations. Among other things, this is particularly
useful for:
* Ensuring data integrity when performing arithmetic operations ;
* Parsing input data of unknown type ;
* Validating data before storage or transmission.
Here is an example of a real use case: when serializing data, it is currently challenging
to determine whether a numeric value can be safely represented as a float or an integer
without loss of precision:
9007199254740993];
if (/* how do I make sure the price can be represented in JSON? */) {
// handle error, e.g., log it or throw an exception
// or cast to string
}
// with this RFC
$data = ['price' => 9007199254740993];
if (!is_representable_as_float($data['price'])) {
$data['price'] = (string) $data['price'];
}
?>
===== Proposal =====
Add the following two functions:
* function is_representable_as_float(mixed $value): bool
* function is_representable_as_int(mixed $value): bool
**When does is_representable_as_float() return true?**
* The value is a float
* The value is an integer that can be converted to a float without loss of precision
* The value is a string that represents a valid floating-point number (e.g., "3.14", "1e10", "-0.001")
* The value is a string that represents a valid integer (e.g., "42", "-7") and can be converted to a float without loss of precision
About the loss of precision: it is important to know that IEEE 754 floating-point numbers can represent integers
exactly up to 2^53 - 1 (inclusive) because the mantissa has 52 bits of precision plus an implicit leading bit.
Therefore, any integer within this range can be represented as a float without loss of precision. An integer
outside this range could lose precision when converted to a float. This is called the "safe integer range"
for floats. MDN provides a good overview of this topic[1].
IEEE 754 floating-point numbers can represent all consecutive integers exactly up to 2^53 - 1
(inclusive). Beyond this range, only specific integers can be represented exactly, following
a pattern based on the available mantissa bits:
* From 2^53 to 2^54: only even numbers (multiples of 2) ;
* From 2^54 to 2^55: only multiples of 4 ;
* From 2^55 to 2^56: only multiples of 8 ;
* And so on...
Additionally, pure powers of 2 (2^0, 2^1, 2^2, ..., up to 2^1023) can always be represented exactly
because they require only a single bit in the mantissa.
The function returns true for any value that can be exactly represented as a float, including these
special cases.
**When does is_representable_as_int() return true?**
* The value is an integer ;
* The value is a float not greater than PHP_INT_MAX and not less than PHP_INT_MIN, without fractional part ;
* The value is a string that represents a valid integer (e.g., "42", "-7") and can be converted to an integer within the range of PHP_INT_MIN and PHP_INT_MAX.
The function behavior depends on the platform's integer range:
is_representable_as_int(2.0**31); // true on 64-bit, false on 32-bit platforms
is_representable_as_int('2147483648'); // true on 64-bit, false on 32-bit platforms
More examples of the function return values:
is_representable_as_int(42); // true
is_representable_as_int(3.14); // false, has a fractional part
is_representable_as_int("123"); // true
is_representable_as_int("123.456"); // false, has a fractional part
is_representable_as_int("1e10"); // true on 64-bit platforms, false on 32-bit platforms
is_representable_as_int("1e400"); // false, not exactly representable as int
==== Naming ====
It's still unsure if the current naming of functions are a prefect fit. Here are a few other propositions to name the new functions:
* is_safe_float() / is_safe_int()
* fits_float() / fits_int()
* can_cast_to_float() / can_cast_to_int()
===== Examples =====
9007199254740993];
if (!is_representable_as_float($data['price'])) {
// handle error, e.g., log it or throw an exception
// or cast to string!
$data['price'] = (string) $data['price'];
}
// validate data from external API
function validateNumericInput($value) {
if (is_numeric($value) && is_representable_as_int($value)) {
return (int) $value;
}
// ...
}
// edge cases
is_representable_as_float(INF); // true
is_representable_as_float(-INF); // true
is_representable_as_float(NAN); // true
is_representable_as_float(""); // false
is_representable_as_float(null); // false
is_representable_as_float(false); // false
is_representable_as_float(true); // false
is_representable_as_float(" 42 "); // true, like "is_numeric()"
is_representable_as_float("\r42\n"); // true, like "is_numeric()"
is_representable_as_float("0x2A"); // false, like "is_numeric()"
is_representable_as_float("0b01110"); // false, like "is_numeric()"
is_representable_as_int(2.0); // true
is_representable_as_int(-0.0); // true
class MyClass {
public function __toString() {
return "42";
}
}
is_representable_as_int(new MyClass()); // true because of __toString()
is_representable_as_float(new MyClass()); // true as well
?>
Note: these functions use locale-independent parsing, similar to is_numeric().
Decimal separator is always "." regardless of locale settings.
===== Backward Incompatible Changes =====
Existing code that declares a function with the same name will result in an error. However, this is unlikely to
happen. A quick search on GitHub with the function names shows no results.
===== Proposed PHP Version(s) =====
Next minor version, likely PHP 8.6
===== Voting Choices =====
* Yes
* No
===== Patches and Tests =====
The pull request is available here: https://github.com/php/php-src/pull/19308
===== References =====
[1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number#number_encoding