Вопрос

Смотрите этот код:

<html>
  <head> 
    <script src="http://www.json.org/json2.js" type="text/javascript"></script>
    <script type="text/javascript">

      var jsonString = '{"id":714341252076979033,"type":"FUZZY"}';
      var jsonParsed = JSON.parse(jsonString);
      console.log(jsonString, jsonParsed);

    </script>
  </head>
  <body>
  </body>
</html>

Когда я вижу свою консоль в Firefox 3.5, значение jsonParsed равно:

Object id=714341252076979100 type=FUZZY

Т.е. число округлено.Пробовал разные значения, результат тот же (число округлено).

Я также не понимаю его правил округления.714341252076979136 округляется до 714341252076979200, тогда как 714341252076979135 округляется до 714341252076979100.

Редактировать: Смотрите первый комментарий ниже.По-видимому, речь идет не о JSON, а о чем-то, связанном с обработкой чисел JavaScript.Но вопрос остается:

Почему это происходит?

Это было полезно?

Решение

То, что вы видите здесь, на самом деле является эффектом двух округлений.Числа в ECMAScript внутренне представлены с плавающей запятой двойной точности.Когда id установлено значение 714341252076979033 (0x9e9d9958274c359 в шестнадцатеричном формате), ему фактически присваивается ближайшее представимое значение двойной точности, которое является 714341252076979072 (0x9e9d9958274c380).Когда вы распечатываете значение, оно округляется до 15 значащих десятичных цифр, что дает 14341252076979100.

Другие советы

Вы переполняете возможности числового типа JavaScript, см. §8.5 спецификации для получения подробной информации.Эти идентификаторы должны быть строками.

IEEE-754 с плавающей запятой двойной точности (тип числа, который использует JavaScript) не может точно представлять ВСЕ цифры (конечно же).Классно, 0.1 + 0.2 == 0.3 является ложным.Это может повлиять на целые числа точно так же, как на дробные;это начинается, как только вы поднимаетесь выше 9,007,199,254,740,991 (Number.MAX_SAFE_INTEGER).

За пределами Number.MAX_SAFE_INTEGER + 1 (9007199254740992), формат IEEE-754 с плавающей запятой больше не может представлять каждое последовательное целое число. 9007199254740991 + 1 является 9007199254740992, но 9007199254740992 + 1 является также 9007199254740992 потому что 9007199254740993 не может быть представлен в этом формате.Следующее, что может быть, это 9007199254740994.Тогда 9007199254740995 не может быть, но 9007199254740996 может.

Причина в том, что у нас закончились биты, поэтому у нас больше нет бита 1s;младший бит теперь представляет собой числа, кратные 2.В конце концов, если мы будем продолжать в том же духе, мы потеряем эту часть и будем работать только в количестве, кратном 4.И так далее.

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


Если вам интересны эти фрагменты, вот что происходит:Двоичное число с плавающей запятой двойной точности стандарта IEEE-754 имеет знаковый бит, 11 бит показателя степени (который определяет общий масштаб числа в степени 2 [поскольку это двоичный формат]) и 52 бита значения (но формат настолько умен, что из этих 52 бит получается 53 бита точности).То, как используется показатель степени, является сложным (описанный здесь), но в очень в расплывчатых терминах, если мы добавим единицу к показателю, значение значимого значения удвоится, поскольку показатель степени используется для степеней 2 (опять же, будьте осторожны, это не прямое значение, в этом есть хитрость).

Итак, давайте посмотрим на значение 9007199254740991 (он же, Number.MAX_SAFE_INTEGER):

   +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− sign bit
  / +−−−−−−−+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− exponent
 / /        |  +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+− significand
/ /         | /                                                  |
0 10000110011 1111111111111111111111111111111111111111111111111111
                = 9007199254740991 (Number.MAX_SAFE_INTEGER)

Это значение экспоненты, 10000110011, означает, что каждый раз, когда мы добавляем единицу к значимому значению, представленное число увеличивается на 1 (целое число 1, мы потеряли способность представлять дробные числа намного раньше).

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

   +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− sign bit
  / +−−−−−−−+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− exponent
 / /        |  +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+− significand
/ /         | /                                                  |
0 10000110100 0000000000000000000000000000000000000000000000000000
                = 9007199254740992 (Number.MAX_SAFE_INTEGER + 1)

Что ж, это нормально, потому что 9007199254740991 + 1 является 9007199254740992 в любом случае.Но!Мы не можем представлять 9007199254740993.У нас закончились кусочки.Если мы добавим только 1 к значимому значению, это добавит 2 к значению:

   +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− sign bit
  / +−−−−−−−+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− exponent
 / /        |  +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+− significand
/ /         | /                                                  |
0 10000110100 0000000000000000000000000000000000000000000000000001
                = 9007199254740994 (Number.MAX_SAFE_INTEGER + 3)

Формат просто больше не может представлять нечетные числа, поскольку мы увеличиваем значение, показатель слишком велик.

В конце концов, у нас снова заканчиваются значимые биты, и нам приходится увеличивать показатель степени, так что в итоге мы можем представлять только числа, кратные 4.Затем кратно 8.Затем кратно 16.И так далее.

Это не вызвано этим анализатором json.Просто попробуйте ввести 714341252076979033 в консоль fbug.Вы увидите тот же номер 714341252076979100.

Смотрите это сообщение в блоге для получения подробной информации:http://www.exploringbinary.com/print-precision-of-floating-point-integers-varies-too

JavaScript использует значения с плавающей запятой двойной точности, то есть общую точность в 53 бита, но вам нужно

ceil(lb 714341252076979033) = 60

биты для точного представления значения.

Ближайшим точно представимым числом является 714341252076979072 (запишите исходное число в двоичном формате, замените последние 7 цифр на 0 и округлить, потому что самая высокая замененная цифра была 1).

Ты получишь 714341252076979100 вместо этого числа, потому что ToString() как описано в ECMA-262, §9.8.1 работает со степенями десяти, и с точностью до 53 бит все эти числа равны.

Проблема в том, что ваш номер требует большей точности, чем у JavaScript.

Можете ли вы отправить номер в виде строки?Разделенный на две части?

JavaScript может обрабатывать точные целые числа только до примерно 9000 миллионов миллионов (это 9 с 15 нулями).Выше этого, и вы получите мусор.Обойдите это, используя строки для хранения чисел.Если вам нужно выполнить математические вычисления с этими числами, напишите свои собственные функции или посмотрите, сможете ли вы найти библиотеку для них:Я предлагаю первое, поскольку мне не нравятся библиотеки, которые я видел.Чтобы вы могли начать, ознакомьтесь с двумя моими функциями на другой ответ.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top