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: TBD
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:
<?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']; } ?>
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 that is not NaN or infinity
- 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]. Because of this, the function return value is also platform-dependent:
is_representable_as_float(PHP_INT_MAX); // true on 64-bit platforms, false on 32-bit platforms
As said earlier, 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.
<?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); // false (overflow to infinity) ?>
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.
Here also, the function return value is platform-dependent:
is_representable_as_int(2**31); // true on 64-bit platforms, 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
<?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); // false is_representable_as_float(-INF); // false is_representable_as_float(NAN); // false 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
Patches and Tests
I'll take care of the implementation for this RFC.