[PHP-DEV] [RFC] Add "is_representable_as_float()" and "is_representable_as_int()" functions

On Tue, Aug 26, 2025, at 20:23, Tim Düsterhus wrote:

Hi

On 8/26/25 19:14, Alexandre Daubois wrote:

3.14 is not exactly representable as a IEEE-754 double-precision
floating point number. The two nearest representable values are
3.14000000000000012434 (this one is the nearest) and
3.13999999999999968026. Returning true for
is_representable_as_float("3.14") is therefore wrong to me.

You’re right about mathematical precision. Perhaps the function name
is misleading. What the function actually should check is whether a
string > float > string roundtrip preserves the value as PHP
developers would expect it, not whether it’s mathematically exactly
representable in IEEE-754.

Maybe a more accurate name would better reflect this behavior. Naming
is super hard again here. The goal is pragmatic: “will this value
survive a type conversion cycle without surprising changes?”

“Surprising changes” is not a meaningful term. Keep in mind that the
behavior of the function will need to be accurately documented and that
folks need something to work with to determine whether a report is valid
or not when bugs in the function get reported - which will inevitably
happen.

In fact to use one of the examples for the RFC: 1e10 does not roundtrip.

php > var_dump((string)(float)“1e10”);
string(11) “10000000000”

“1e10” clearly is different than “10000000000”.

Generally speaking, I’m also not sure if “printing raw floats” is a
use-case we should encourage. Instead almost any application will need
to format a number for a specific use-case. Formatting numbers for human
consumption [1] has different requirements compared to formatting
numbers for programmatic consumption.

Indeed. In some parts of the world, 1.000,01 is interpreted as 1000.01 in computers. That format also doesn’t roundtrip but is a valid numeric string, depending on locale.

— Rob

Hi

On 8/26/25 16:48, Larry Garfield wrote:

I have multiple times just recently had need of "I have a numeric string, should I cast it to an int or a float?", for which an is_representable_as_int() function (or similar) would be quite helpful, and neater than the messy solution I usually use.

It would've been nice to know what that use-case is, rather than just knowing that you had that use-case.

I'm having a hard time thinking of something where I don't a-priori know what type I expect to get and would need to inspect the value to make a decision.

I see how having a function that safely coerces a string into an int, returning null if coercion fails, basically intval() with better error handling and taking only `string`s, could be useful, but that's not what is being asked here.

Best regards
Tim Düsterhus

On Tue, Aug 26, 2025, at 1:36 PM, Tim Düsterhus wrote:

Hi

On 8/26/25 16:48, Larry Garfield wrote:

I have multiple times just recently had need of "I have a numeric string, should I cast it to an int or a float?", for which an is_representable_as_int() function (or similar) would be quite helpful, and neater than the messy solution I usually use.

It would've been nice to know what that use-case is, rather than just
knowing that you had that use-case.

I'm having a hard time thinking of something where I don't a-priori know
what type I expect to get and would need to inspect the value to make a
decision.

I see how having a function that safely coerces a string into an int,
returning null if coercion fails, basically intval() with better error
handling and taking only `string`s, could be useful, but that's not what
is being asked here.

Best regards
Tim Düsterhus

When doing generic serialization, the input is often always-strings (eg, environment variables, HTTP Query parameters, etc.) When doing generic code (not type generics, but "works on anything" kind of generic), I often have to resort to this:

Which gets the job done, but feels ugly to me.

Even if I know what the target type is, I still need to ask the question "so does this string match the target type?"

"I have a string, the parameter wants an int, is that even possible?" Being able to replace that floor() nonsense with is_integerable() (by whatever name) would make my life a lot easier.

For float, is_numeric() is already sufficient for my purposes. I just need to be able to differentiate between "3" and "3.14" to cast to the correct type.

--Larry Garfield

Le mar. 26 août 2025 à 21:38, Larry Garfield <larry@garfieldtech.com> a écrit :

On Tue, Aug 26, 2025, at 1:36 PM, Tim Düsterhus wrote:

Hi

On 8/26/25 16:48, Larry Garfield wrote:

I have multiple times just recently had need of “I have a numeric string, should I cast it to an int or a float?”, for which an is_representable_as_int() function (or similar) would be quite helpful, and neater than the messy solution I usually use.

It would’ve been nice to know what that use-case is, rather than just
knowing that you had that use-case.

I’m having a hard time thinking of something where I don’t a-priori know
what type I expect to get and would need to inspect the value to make a
decision.

I see how having a function that safely coerces a string into an int,
returning null if coercion fails, basically intval() with better error
handling and taking only strings, could be useful, but that’s not what
is being asked here.

Best regards
Tim Düsterhus

When doing generic serialization, the input is often always-strings (eg, environment variables, HTTP Query parameters, etc.) When doing generic code (not type generics, but “works on anything” kind of generic), I often have to resort to this:

https://github.com/Crell/EnvMapper/blob/master/src/EnvMapper.php#L88

Which gets the job done, but feels ugly to me.

Even if I know what the target type is, I still need to ask the question “so does this string match the target type?”

https://github.com/Crell/Carica/blob/master/src/Middleware/NormalizeArgumentTypesMiddleware.php#L73

“I have a string, the parameter wants an int, is that even possible?” Being able to replace that floor() nonsense with is_integerable() (by whatever name) would make my life a lot easier.

For float, is_numeric() is already sufficient for my purposes. I just need to be able to differentiate between “3” and “3.14” to cast to the correct type.

–Larry Garfield

Why not just 0 + $theValue? Then the engine will decide. No?

On Tue, Aug 26, 2025, at 21:36, Larry Garfield wrote:

On Tue, Aug 26, 2025, at 1:36 PM, Tim Düsterhus wrote:

Hi

On 8/26/25 16:48, Larry Garfield wrote:

I have multiple times just recently had need of “I have a numeric string, should I cast it to an int or a float?”, for which an is_representable_as_int() function (or similar) would be quite helpful, and neater than the messy solution I usually use.

It would’ve been nice to know what that use-case is, rather than just
knowing that you had that use-case.

I’m having a hard time thinking of something where I don’t a-priori know
what type I expect to get and would need to inspect the value to make a
decision.

I see how having a function that safely coerces a string into an int,
returning null if coercion fails, basically intval() with better error
handling and taking only strings, could be useful, but that’s not what
is being asked here.

Best regards
Tim Düsterhus

When doing generic serialization, the input is often always-strings (eg, environment variables, HTTP Query parameters, etc.) When doing generic code (not type generics, but “works on anything” kind of generic), I often have to resort to this:

https://github.com/Crell/EnvMapper/blob/master/src/EnvMapper.php#L88

Which gets the job done, but feels ugly to me.

Even if I know what the target type is, I still need to ask the question “so does this string match the target type?”

https://github.com/Crell/Carica/blob/master/src/Middleware/NormalizeArgumentTypesMiddleware.php#L73

“I have a string, the parameter wants an int, is that even possible?” Being able to replace that floor() nonsense with is_integerable() (by whatever name) would make my life a lot easier.

For float, is_numeric() is already sufficient for my purposes. I just need to be able to differentiate between “3” and “3.14” to cast to the correct type.

–Larry Garfield

Isn’t this the entire use-case of type coercion?

— Rob

On 26 August 2025 19:23:20 BST, "Tim Düsterhus" <tim@bastelstu.be> wrote:

"Surprising changes" is not a meaningful term. Keep in mind that the behavior of the function will need to be accurately documented and that folks need something to work with to determine whether a report is valid or not when bugs in the function get reported - which will inevitably happen.

I think there's a sliding scale of "strictness" of casts, with string to float probably the hardest to pin down.

At one end, you have "purely lossless" or "symmetrical" - where (string)(float)$x === $x This would mean accepting '4' but not '4.0', which is probably not what users would expect.

From there, you can add some "normalisation" - ignore insignificant zeros at start and end; perhaps also ignore leading and trailing spaces.

Then you get alternative representations - most commonly, scientific notation like '2.5E10'. This hugely increases the number of inputs that result in the same output, e.g. '0.2E1' is yet another synonym for '2'.

Then there are inputs which don't fully match the normal format, but are unambiguous - '.1', '1.', '+1', etc

At the other end of the scale, there's the "best guess" approach that PHP's explicit cast operators use - (string)(float)' +4.2E1 bananas ' results in '42'

And that's *before* we check the precision of the resulting interpretation, and how close the floating point value is to the target decimal.

I'd really like something stricter than is_numeric but more flexible than ctype_digit; but exactly what it should do is a bit of a minefield.

Rowan Tommins
[IMSoP]

Hi,

You pointed out many valid issues here. After taking a few steps back,
I wonder indeed if Gina's proposition [1] wouldn't be enough.
Also, as Rowan said, knowing what these functions should exactly do is
complex and I'm afraid we won't find a happy middle ground that suits
everyone and every situation. Based on this thread feedback and
suggestions, the use cases seem niche, for is_representable_as_float()
at least.

Best,
Alexandre Daubois

[1] Introduce PHP_INT_MAX_SAFE constant similar to JS's Number.MAX_SAFE_INTEGER constant - Externals

Hi,

I would like to present the RFC to add the
"is_representable_as_float()" and "is_representable_as_int()"
functions. These functions provide developers with a way to check
whether values can be losslessly converted between integer and
floating-point representations.

PHP: rfc:is-representable-as-float-int

The RFC has been withdrawn. I explained it a bit here (sent to the
wrong thread, sorry about that):

Thanks,

— Alexandre Daubois