strtod and NaN vs. zero

php.internals

Marc Bennewitz

9 years ago
Hi internals, On casting a non numeric value to a float in PHP the result will be float(0). In PHP-7.0 an exception was introduced that on casting a string "/\s*NaN\s*/i" will result in float(NaN). https://3v4l.org/2Xakm Wouldn't it be more logical and expected to return NaN in all cases of casting a non numeric string to a floating point number? Thanks Marc

Niklas Keller

9 years ago
2016-10-23 12:55 GMT+02:00 Marc Bennewitz <dev@mabe.berlin>:
> Hi internals, > > On casting a non numeric value to a float in PHP the result will be > float(0). > > In PHP-7.0 an exception was introduced that on casting a string > "/\s*NaN\s*/i" will result in float(NaN). > > https://3v4l.org/2Xakm > > Wouldn't it be more logical and expected to return NaN in all cases of > casting a non numeric string to a floating point number? > > Thanks > Marc > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > >
+1 Regards, Niklas

Marc Bennewitz

9 years ago
Am 23.10.2016 um 13:49 schrieb Niklas Keller:
> 2016-10-23 12:55 GMT+02:00 Marc Bennewitz <dev@mabe.berlin > <mailto:dev@mabe.berlin>>: > > Hi internals, > > On casting a non numeric value to a float in PHP the result will be > float(0). > > In PHP-7.0 an exception was introduced that on casting a string > "/\s*NaN\s*/i" will result in float(NaN). > > https://3v4l.org/2Xakm > > Wouldn't it be more logical and expected to return NaN in all cases > of casting a non numeric string to a floating point number? > > Thanks > Marc > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > > > +1
I just noticed that the behavior change in PHP-7 has been "fixed" already by https://bugs.php.net/bug.php?id=73329. So the old behavior will be presented soon. But I'm still curious why casting any non numeric string results in the valid number float(0) where there is a special value in floating point numbers declared to represent not a number values. I also did a small research on other languages and found the following: C strtod returns float(0) -> but supports converting the strings "INFINITY" or "NAN" (case-insensitive) JavaScript returns NaN Python throws ValueError: could not convert string to float -> but supports the string +/- "NaN", "Inf", "Infinity" (case-insensitive) Perl returns float(0) -> but supports the string +/- "NaN", "Inf", "Infinity" (case-insensitive) So there is a nice mix of how other languages will handle this problem. In my opinion it still makes much more sense to return NaN in any case a not numeric string will be casted to float incl. "Inf" and "Infinity". (If spaces should be ignored or suffixes allowed is another question) = The beginning of a string must contain at least one number by ignoring spaces to be casted to a valid floating point number. In any other situations NaN must be returned because this represents what it is = Not a Number. So for me the following example table makes sense: In -> Out "" -> NaN " " -> NaN "+" -> NaN "-" -> NaN "." -> NaN ".5" -> 0.5 "0.5" -> 0.5 " 0.5" -> 0.5 "0" -> 0 " 0" -> 0 "Nan" -> NaN "Inf" -> NaN "Info" -> NaN "foo" -> NaN ".foo" -> NaN ".2foo" -> 0.2 " .2foo" -> 0.2 "foo1" -> NaN "foo." -> NaN "foo0.1" -> NaN Thoughts Marc

Marc Bennewitz

9 years ago
Am 23.10.2016 um 21:13 schrieb Marc Bennewitz:
> > > Am 23.10.2016 um 13:49 schrieb Niklas Keller: >> 2016-10-23 12:55 GMT+02:00 Marc Bennewitz <dev@mabe.berlin >> <mailto:dev@mabe.berlin>>: >> >> Hi internals, >> >> On casting a non numeric value to a float in PHP the result will be >> float(0). >> >> In PHP-7.0 an exception was introduced that on casting a string >> "/\s*NaN\s*/i" will result in float(NaN). >> >> https://3v4l.org/2Xakm >> >> Wouldn't it be more logical and expected to return NaN in all cases >> of casting a non numeric string to a floating point number? >> >> Thanks >> Marc >> >> -- >> PHP Internals - PHP Runtime Development Mailing List >> To unsubscribe, visit: http://www.php.net/unsub.php >> >> >> +1 > > I just noticed that the behavior change in PHP-7 has been "fixed" > already by https://bugs.php.net/bug.php?id=73329. > > So the old behavior will be presented soon. > > But I'm still curious why casting any non numeric string results in the > valid number float(0) where there is a special value in floating point > numbers declared to represent not a number values. > > I also did a small research on other languages and found the following: > > C strtod returns float(0) > -> but supports converting the strings "INFINITY" or "NAN" > (case-insensitive) > > JavaScript returns NaN
I forgot to mention that in JavaScript (parseFloat) it is nearly the behavior I would expect. The only difference is that it allows the string +/- "Infinity" to be casted to Infinity.

Andrew Faulds

9 years ago
Hi, Marc Bennewitz wrote:
> > But I'm still curious why casting any non numeric string results in the > valid number float(0) where there is a special value in floating point > numbers declared to represent not a number values.
Because that's what C's strtod() does, and it's also what (int) does (because that's what C's strtol()) does. There'd be an argument for using NaN, sure, but it wouldn't be consistent with the rest of the language. Generally, PHP assumes that a string with no leading digits has the number value of zero. This is true not only in (int) and (float) casts, but also in generic number coercion operations, like with +, -, *, / and **. To change the behaviour of (float) here would be to introduce a new inconsistency to PHP. If we go for NaN everywhere, then we introduce a different problem: (int) should return an integer, and the generic number coercions prefer giving you an integer rather than a float if appropriate. Producing a NaN here would mean you'd now get an int rather than a float, and would cause a cascading change in operation result types which could lead to previously working code now throwing lots of E_WARNINGs or TypeErrors (NaN is not accepted for integer type declarations in weak mode, and all floats are not accepted for integer type declarations in strict mode). Thanks.
-- Andrea Faulds https://ajf.me/

Marc Bennewitz

9 years ago
Hi Andrea, Am 24.10.2016 um 15:40 schrieb Andrea Faulds:
> Hi, > > Marc Bennewitz wrote: >> >> But I'm still curious why casting any non numeric string results in the >> valid number float(0) where there is a special value in floating point >> numbers declared to represent not a number values. > > Because that's what C's strtod() does, and it's also what (int) does > (because that's what C's strtol()) does. > > There'd be an argument for using NaN, sure, but it wouldn't be > consistent with the rest of the language. Generally, PHP assumes that a > string with no leading digits has the number value of zero. This is true > not only in (int) and (float) casts, but also in generic number coercion > operations, like with +, -, *, / and **. To change the behaviour of > (float) here would be to introduce a new inconsistency to PHP. If we go > for NaN everywhere, then we introduce a different problem: (int) should > return an integer, and the generic number coercions prefer giving you an > integer rather than a float if appropriate. Producing a NaN here would > mean you'd now get an int rather than a float, and would cause a > cascading change in operation result types which could lead to > previously working code now throwing lots of E_WARNINGs or TypeErrors > (NaN is not accepted for integer type declarations in weak mode, and all > floats are not accepted for integer type declarations in strict mode).
Thanks for your explanation. It's totally true that on casting non numeric string to integer will result in 0 but that's a different type even if it's also a numeric type. From my understanding PHP tries to cast from one type to another by loosing as less information as possible. In this case I see a non numeric string that gets casted to a float will loose exactly this information that it's "Not a Number" where it would be possible to keep this information. On casting this to int there is of course no possibility to keep it but I don't see where it's inconsistent as it's a different type used for different use-cases. Having mathematical operations with non numeric strings should result in the same behavior as NaN will result in NaN which makes sense and I don't see where it will throw lots on WARNINGS or TypeErrors as it's still a float. Since PHP-7.0 there was a WARNING introduced "A non-numeric value encountered" which is nice but the result is still a valid float where "Not a Number" would be more logical. That's of course my personal opinion. Of course changing this would introduce a BC break and therefore should be reviewed safely. But the bug #73329 shows that there is not much attention as it stayed for some versions. This mail is not an RFC not even a proposal it's just mail to get an answered why (Thanks again) and to see how others think about.
> > Thanks. >
Thanks Marc

Andrew Faulds

9 years ago
Hi, Marc Bennewitz wrote:
> It's totally true that on casting non numeric string to integer will > result in 0 but that's a different type even if it's also a numeric type. > > From my understanding PHP tries to cast from one type to another by > loosing as less information as possible. In this case I see a non > numeric string that gets casted to a float will loose exactly this > information that it's "Not a Number" where it would be possible to keep > this information. On casting this to int there is of course no > possibility to keep it but I don't see where it's inconsistent as it's a > different type used for different use-cases.
(int) and (float) are lossy and have always been so. If you want PHP to complain when you give it something non-numeric, rather than coerce to 0, there are alternatives.
> Having mathematical operations with non numeric strings should result in > the same behavior as NaN will result in NaN which makes sense and I > don't see where it will throw lots on WARNINGS or TypeErrors as it's > still a float.
Because previously it would be an integer. "foo" + "bar" currently gives an integer 0. Now it would give you a float NaN. That has potential knock-on effects. Thanks.
-- Andrea Faulds https://ajf.me/

Anatol Belski

9 years ago
Hi Marc,
> -----Original Message----- > From: Marc Bennewitz [mailto:dev@mabe.berlin] > Sent: Sunday, October 23, 2016 12:56 PM > To: PHP Internals List <internals@lists.php.net> > Subject: [PHP-DEV] strtod and NaN vs. zero > > Hi internals, > > On casting a non numeric value to a float in PHP the result will be float(0). > > In PHP-7.0 an exception was introduced that on casting a string "/\s*NaN\s*/i" > will result in float(NaN). > > https://3v4l.org/2Xakm >
It is a BC breaching bug and was fixed https://bugs.php.net/bug.php?id=73329 . Cast of string to number is always zero, a way to change it would cause another BC breach which IMHO were not worthwhile. Regards Anatol