Почему PHP и JavaScript имеют проблемы с восьмеричными и шестнадцатеричными числами?

Я заметил, что PHP и JavaScript обрабатывают восьмеричные и шестнадцатеричные числа с некоторой трудностью при манипулировании типами и литье:

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

Я знаю, что PHP имеет функции octdec() и hexdec() для исправления восьмеричного/шестнадцатеричного неправильного поведения, но я ожидаю, что intval() будет иметь дело с восьмеричными и шестнадцатеричными числами, как это делает JavaScript parseInt().

Во всяком случае, в чем причина этого нечетного поведения?

Ответ 1

Представьте, что кто-то определяет 035 как количество для некоторого продукта для покупки (ведущий 0 предназначен только для заполнения, чтобы он соответствовал другим трехзначным количествам в списке). Ожидается, что 035 будет интерпретироваться так же, как 35 для не-программиста. Но если PHP должен интерпретировать восьмеричные числа в строках, результат внезапно будет 29 = > WTF?!? С другой стороны, шестнадцатеричная нотация - это не проблема, потому что люди обычно не указывают числа, используя нотацию 0x23.

Это, кстати, происходит не только с конечными пользователями, но и с программистами. Часто программисты пытаются заполнить свои номера ведущими нулями и - да, все не так! Именно поэтому JS больше не допускает восьмеричную запись в строгом режиме, а другие языки используют более явный префикс 0o.

Кстати, я согласен, что это поведение непоследовательно. На мой взгляд, шестнадцатеричное обозначение также не должно анализироваться. Точно так же, как восьмеричная и двоичная нотации. Особенно учитывая, что явное приведение (int) не анализирует шестнадцатеричный код, а вместо этого просто считывает все до первой нецифровой.


Обращаясь к случаю intval, он фактически ведет себя так же, как и документально: intval не существует для разбора собственных целых чисел PHP, это для синтаксического анализа целых чисел указанной базы. Если вы посмотрите на docs, вы обнаружите, что он принимает второй аргумент $base, который по умолчанию равен 10. (Отбрасывание (int) путем внутренней привязки к такому же convert_to_long_base с помощью base = 10, поэтому оно всегда будет вести себя точно так же intval.)

Ответ 2

В javascript только десятичные и шестнадцатеричные значения определены как часть стандарта, тогда как восьмеричное значение зависит от реализации, что объясняет, почему восьмой синтаксический разбор не является согласованным между приведенными вами примерами.

Вы можете избавиться от восьмеричных литералов в строгом режиме, но во всех проверенных вами браузерах parseInt по-прежнему пытается проанализировать восьмеричную, а не десятичную. Что странно, потому что спецификация ничего не говорит о попытке интерпретировать подразумеваемый восьмеричный для parseInt и явно запрещает восьмеричное расширение в строгом режиме. Таким образом, нет восьмеричных литералов, ничего в спецификации о попытке превратить "010" в восьмеричную, когда parseInt 'd, и поведение сохраняется даже в строгом режиме.

Итак, Number("012") === 12 верен, а parseInt("012") === 10 неверен в соответствии с моими интерпретациями спецификации, которые вы можете читать здесь

Существует хорошая причина для шестнадцатеричного, но он значительно упрощает операции над числами на уровне бит. И "0xFF" - это не то, что кто-то называет, если он не означает гексагон.

Ответ 3

Не прочитал другой ответ, но, по крайней мере, в PHP нет проблем с восьмеричными или шестнадцатеричными числами; вы просто делаете это неправильно

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

Приведение строки в целое число будет... да, отбросить ее до целого числа так, как это всегда делает PHP: он примет любое число и сформирует целое число из него, пока не найдет какой-либо нечисловой символ. В этом случае его единственный 0

hexdec() работает с строками, но эти строки шестнадцатеричны только без префикса 0x.

echo hexdec('A0`); // 16

Префикс 0 (восьмеричный) и 0x (шестнадцатеричный) существует, чтобы различать разные целочисленные обозначения друг от друга, но пока вы пишете его как строку, PHP будет рассматривать его как строку.

Я предполагаю, что вы сделали аналогичную ошибку с javascript.