Copied from wiki.php.net, last synchronized Oct 13, 2026 11:53 UTC
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:
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:
<?php
// 9007199254740993 is NOT representable as a float
// currently, we can use is_numeric() to check if a value is numeric
// but this does not guarantee that the value can be represented as a float or an int
$data = ['price' => 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'];
}
?>
Add the following two functions:
function is_representable_as_float(mixed $value): boolfunction is_representable_as_int(mixed $value): boolWhen does is_representable_as_float() return true?
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:
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.
<?php
is_representable_as_float(2**53); // true
is_representable_as_float(2**53 + 1); // false, precision loss when cast to float
is_representable_as_float(42); // true
is_representable_as_float("123.456"); // true
is_representable_as_float("1e308"); // false, not exactly representable as float
is_representable_as_float("1e400"); // false
// beyond safe range
is_representable_as_float(2**54); // true (exact power of 2)
is_representable_as_float(2**54 + 2); // true (multiple of 2 in this range)
is_representable_as_float(2**54 + 1); // false (odd number, not representable)
// large pure powers of 2
is_representable_as_float(2**200); // true (exact power of 2)
is_representable_as_float(2**1023); // true (largest normal float)
is_representable_as_float(2**1024); // true (overflow to infinity)
?>
When does is_representable_as_int() return true?
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
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()<?php
// validate before serialization
$data = ['price' => 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.
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.
Next minor version, likely PHP 8.6
Vote: Implement — Yes: 0 · No: 0 · Abstain: 0 (closed)
The pull request is available here: https://github.com/php/php-src/pull/19308