Подтвердить что ты не робот

Почему parseInt ('dsff66', 16) возвращает 13?

сегодня я наткнулся на странный (на мой взгляд) случай в JavaScript. Я передал не шестнадцатеричную строку в функцию parseInt с базой 16 и... Я получил результат. Я ожидал бы, что функция закроет какое-то исключение или, по крайней мере, вернет NaN, но ему удалось разобрать его и вернуть int.

Мой вызов:

var parsed = parseInt('dsff66', 16); // note the 's' in the first argument
document.write(parsed);
4b9b3361

Ответ 1

Зачем кому-то нужно, чтобы эта функция вела себя так (верните целое число, даже если это не точное представление переданной строки)?

Потому что большую часть времени (безусловно) вы работаете с базовыми номерами 10, и в этом случае JS может просто отличить - не разобрать - строку к числу. (edit: По-видимому, не только base-10, см. обновление ниже.)

Так как JS динамически типизирован, некоторые строки работают отлично, как числа без какой-либо работы с вашей стороны. Например:

 "21" / 3;   // => 7
 "12.4" / 4; // => 3.1

Нет необходимости в parseInt, потому что "21" и "12.4" уже являются по существу числами. Если, однако, строка была "12.4xyz", тогда вы действительно получили бы NaN при делении, так как это явно не число и не может быть неявно брошено или принуждено к одному.

Вы также можете явно "отличить" строку до номера с помощью Number(someString). Пока он поддерживает только базу 10,, он действительно вернет NaN для недопустимых строк.

Так как JS уже имеет неявный и явный тип casting/conversion/coercion, роль parseInt не должна быть еще одной функцией кастинга.

parseInt роль - это, быть может, функция синтаксического анализа. Функция, которая изо всех сил пытается понять ее вклад, возвращая все, что может. Это, когда у вас есть строка, которую вы не можете просто отличить, потому что она не совсем идеальна. (И, как основной синтаксис JS, он напоминает C, поскольку ответ apsillers объяснялся хорошо.)

И так как это синтаксический анализатор, а не функция литья, он получил дополнительную возможность иметь возможность обрабатывать другие базы, кроме 10.

Теперь вы можете спросить, почему нет строгой функции кастинга, которая обрабатывает номера не-base-10 и будет жаловаться так, как вы хотите, но... эй, там просто нет. Дизайнеры JS просто решили, что parseInt будет достаточно, потому что, опять же, 0x63 процента времени, вы имеете дело с базой 10.

Ближе всего вы можете перейти к "кастингу", вероятно, что-то ужасно хакерское:

var hexString = "dsff66";
var number = eval("0x" + hexString); // attempt to interpret as a hexadecimal literal

который выкинет SyntaxError, потому что 0xdsff66 не является допустимым шестнадцатеричным литералом.

Обновление:. Как отмечает Лекенштейн в комментариях, JS, похоже, правильно добавляет шестнадцатеричные строки 0x -prefixed. Я этого не знал, но, похоже, это работает:

1 * "0xd0ff66"; // => 13696870
1 * "0xdsff66"; // => NaN

что делает его самым простым способом передать шестнадцатеричную строку в число - и получить NaN, если он не может быть правильно представлен.

Такое же поведение относится к Number(), например, Number("0xd0ff66") возвращает целое число, а Number("0xdsff66") возвращает NaN.

(/обновление)

В качестве альтернативы вы можете проверить строку заранее и вернуть NaN при необходимости:

function hexToNumber(string) {
  if( !/^(0x)?[0-9a-f]+$/i.test(string) ) return Number.NaN;
  return parseInt(string, 16);
}

Ответ 2

parseInt считывает ввод до тех пор, пока не встретит недопустимый символ, а затем использует любой действительный ввод, который он считывает до этого недопустимого символа. Рассмотрим:

parseInt("17days", 10);

Это будет использовать вход 17 и опустить все после недопустимого d.

Из спецификация ECMAScript:

Если [входная строка] S содержит любой символ, который не является цифрой radix-R, тогда пусть Z [строка должна быть целочисленной] быть подстрокой в S, состоящей из всех символов перед первым таким символом; в противном случае пусть Z - S.

В вашем примере s является недопустимым символом base-16, поэтому parseInt использует только ведущий d.

Что касается того, почему это поведение было включено: нет никакого способа узнать наверняка, но это, скорее всего, попытка воспроизвести поведение strtol (строка в длину) из стандартной библиотеки C. На странице strtol(3):

... строка преобразуется в значение long int очевидным образом, останавливается при первом символе, который не является допустимой цифрой в данной базе.

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

Ответ 3

В этом конкретном случае parseInt() интерпретируйте букву от "A" до "F" как hexadecimal и проанализируйте ее до десятичных чисел. Это означает, что d вернет 13.

Что делает parseInt()

  • parseInt("string", radix) интерпретировать числа и буквы в строке как шестнадцатеричные (они зависят от числа оснований) от числа.

  • parseInt() только номер или буква синтаксического анализа как шестнадцатеричный с начала строки до тех пор, пока недопустимый символ не станет шестнадцатеричным.

  • Если parseInt() не может найти любое число или букву в качестве шестнадцатеричного в начале строки parseInt(), вернет NaN.

  • Если радиус не определен, радиус 10.

  • Если строка начинается с "0x", радиус 16.

  • Если установленный радиус 0, радиус 10.

  • Если радиус 1, parseInt() возвращает NaN.

  • Если радиус 2, parseInt(), выполните синтаксический разбор "0" и "1".

  • Если радиус 3, parseInt() разобрать только "0", "1" и "2". И так далее.

  • parseInt() parse "0" до 0, если в нем нет номера, и удалите 0, если за ним следует число. например "0" return 0 и "01" return 1.

  • Если радиус 11, parseInt() используется только синтаксическая строка, начинающаяся с номера от "0" до "9" и/или буквы "A".

  • Если радиус 12, parseInt обрабатывает только строку, начинающуюся с номера от "0" до "9" и/или буквы "A" и "B" и т.д.

  • максимальный радиус 36, он будет анализировать строку, начинающуюся с номера от "0" до "9" и/или буквы от "A" до "Z".

  • Если символы, интерпретируемые как шестнадцатеричные, более одного, каждый символ будет иметь другое значение, хотя эти символы являются одним и тем же символом. например parseInt("AA", 11) первый "A" имеет другое значение со вторым "A".

  • Различные radix возвращают другое число, хотя строки - это одна и та же строка.

Посмотрите в действии

document.body.innerHTML = "<b>What parseInt() does</b><br>" + 
                          "parseInt('9') = " + parseInt('9') + "<br>" +
                          "parseInt('0129ABZ', 0) = " + parseInt('0129ABZ', 0) + "<br>" +
                          "parseInt('0', 1) = " + parseInt('0', 1) + "<br>" +
                          "parseInt('0', 2) = " + parseInt('0', 2) + "<br>" +
                          "parseInt('10', 2) = " + parseInt('10', 2) + "<br>" +
                          "parseInt('01', 2) = " + parseInt('01', 2) + "<br>" +
                          "parseInt('1', 2) = " + parseInt('1', 2) + "<br>" +
                          "parseInt('A', 10) = " + parseInt('A', 10) + "<br>" +
                          "parseInt('A', 11) = " + parseInt('A', 11) + "<br>" +
                          "parseInt('Z', 36) = " + parseInt('Z', 36) + "<br><br>" +
                          "<b>The value:</b><br>" +
                          "parseInt('A', 11) = " + parseInt('A', 11) + "<br>" +
                          "parseInt('A', 12) = " + parseInt('A', 12) + "<br>" +
                          "parseInt('A', 13) = " + parseInt('A', 13) + "<br>" +
                          "parseInt('AA', 11) = " + parseInt('AA', 11) + " = 100 + 20" + "<br>" +
                          "parseInt('AA', 12) = " + parseInt('AA', 12) + " = 100 + 30" + "<br>" +
                          "parseInt('AA', 13) = " + parseInt('AA', 13) + " = 100 + 40" + "<br>" +
                          "parseInt('AAA', 11) = " + parseInt('AAA', 11) + " = 1000 + 300 + 30" + "<br>" +
                          "parseInt('AAA', 12) = " + parseInt('AAA', 12) + " = 1000 + 500 + 70" + "<br>" +
                          "parseInt('AAA', 13) = " + parseInt('AAA', 13) + " = 1000 + 700 + 130" + "<br>" +
                          "parseInt('AAA', 14) = " + parseInt('AAA', 14) + " = 1000 + 900 + 210" + "<br>" +
                          "parseInt('AAA', 15) = " + parseInt('AAA', 15) + " = 1000 + 1100 + 310";

Ответ 4

For radices above 10, the letters of the alphabet indicate numerals greater than 9. For example, for hexadecimal numbers (base 16), A through F are used.

В вашей строке dsff66, d является шестнадцатеричным символом (даже если строка не является шестнадцатеричной), которая соответствует типу radix и эквивалентна числу 13. После этого он перестает разбираться, так как следующий символ не является шестнадцатеричным, следовательно, результатом.