Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do PHP and JavaScript have problems dealing with octal and hexadecimal numbers?

I have noticed that PHP and JavaScript treat octal and hexadecimal numbers with some difficulty while type juggling and casting:

PHP:

echo 16 == '0x10' ? 'true' : 'false'; //true, as expected
echo 8  == '010'  ? 'true' : 'false'; //false, o_O

echo (int)'0x10';    //0, o_O
echo intval('0x10'); //0, o_O
echo (int)'010';     //10, o_O
echo intval('010');  //10, o_O

JavaScript:

console.log(16 == '0x10' ? 'true' : 'false'); //true, as expected
console.log(8  == '010'  ? 'true' : 'false'); //false, o_O

console.log(parseInt('0x10')); //16, as expected
console.log(parseInt('010'));  //8, as expected
console.log(Number('0x10'));   //16, as expected
console.log(Number('010'));    //10, o_O

I know that PHP has the octdec() and hexdec() functions to remedy the octal/hexadecimal misbehaviour, but I'd expect the intval() to deal with octal and hexadecimal numbers just as JavaScript's parseInt() does.

Anyway, what is the rationale behind this odd behaviour?

like image 639
mingos Avatar asked Nov 24 '11 19:11

mingos


3 Answers

Imagine somebody specifies 035 as a quantity for some product to buy (the leading 0 is just for padding so it matches other three-digit quantities in the list). 035 is obviously expected to be interpreted just like 35 for a non-programmer. But if PHP were to interpret octal numbers in strings the result would suddenly be 29 => WTF?!? Hexadecimal notation on the other hand is less of a problem because people don't commonly specify numbers using a 0x23 notation.

This by the way doesn't only happen to end users, but to programmers too. Often programmers try to pad their numbers with leading zeros and - huh, everything is wrong! That's why JS doesn't allow octal notation in strict mode anymore and other languages use the more explicit 0o prefix.

By the way, I do agree that this behavior is inconsistent. In my eyes hexadecimal notation shouldn't be parsed either. Just like octal and binary notation is not. Especially considering that the explicit (int) cast doesn't parse hex either and instead just reads everything up to the first non-digit.


Addressing the intval case, it actually behaves just like documented: intval isn't there for parsing PHP's native integer notations, it is for parsing integers of a specified base. If you have a look at the docs, you'll find that it takes a second argument $base which defaults to 10. (The (int) cast by the way internally maps down to the same convert_to_long_base call with base = 10, so it will always behave exactly like intval.)

like image 114
NikiC Avatar answered Oct 20 '22 20:10

NikiC


In javascript, only decimal and hex are defined as part of the standard, while the octal is implementation dependent, which would explain why octal parsing is not consistent between the examples you gave.

You can get rid of octal literals in strict mode but in all browsers I tested, parseInt still tried to parse an octal instead of decimal. Which is kind of strange because the spec does not say anything about trying to interpret implied octal for parseInt and explicitly prohibits the octal extension when in strict mode. So no octal literals, nothing in the spec about trying to turn "010" into an octal when parseInt'd, and the behavior persists even in strict mode.

So Number("012") === 12 is correct while parseInt("012") === 10 is not correct according to my interpretations of the spec which you can read here

There is a good reason for hexadecimal though, it makes operations on numbers at bit level much easier. And "0xFF" is not something someone types if he doesn't mean a hex.

like image 37
Esailija Avatar answered Oct 20 '22 21:10

Esailija


Didn't read the other answer, but at least in PHP there is no problem with octal or hexadecimal numbers; you just doing it wrong

"0x12" // String with content "0x12"
0x12 // Integer "18"
010 // integer "8"

Casting the string to integer will ... yes, cast it to integer the way PHP always does it: It will take any number and form the integer out of it until it founds any non-numeric character. In this case its only 0

hexdec() works on strings, but this strings are hexadecimal only without the prefix 0x.

echo hexdec('A0`); // 16

The prefixes 0 (octal) and 0x (hexadecimal) exists to distinguish the different integer notations from each other, but as long as you write it as a string, PHP will treat it as a string.

I assume, that you did a similar mistake with javascript.

like image 35
KingCrunch Avatar answered Oct 20 '22 20:10

KingCrunch