Большие числа, ошибочно округленные в JavaScript

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

<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. Но остается вопрос:

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

 Jaanus04 сент. 2009 г., 17:40
Спасибо всем за быстрые полезные ответы, хотелось бы отметить все 3 как официальные ответы.

Ответы на вопрос(6)

Решение Вопроса

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

 Monish Chhadwa17 июн. 2019 г., 14:21
Как 15 значащих десятичных цифр «143412520769791» вместо «714341252076979» я не понял

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

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

 Rick Regan05 сент. 2009 г., 00:04
Спасибо за ссылку на мою статью, но она объясняет только половину проблемы - ПЕЧАТЬ внутренне округленного значения. Даже если javascript позволит вам напечатать все это, это все равно будет неправильно - это будет ближайшее представимое значение двойной точности, как объяснено другими ниже.

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

ceil(lb 714341252076979033) = 60

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

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

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

Вы переполняете емкость типа номера 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 Можно.

Причина в том, что у нас закончились биты, поэтому у нас больше нет бита 1с; бит самого низкого порядка теперь представляет кратные 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, мы потеряли способность представлять дробные числа намного раньше).

Но теперь это значение полно. Чтобы пройти это число, мы должны увеличить показатель степени, что означает, что если мы добавим единицу к значениюand, значение представленного числа возрастет на 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. И так далее.

 jsh03 апр. 2013 г., 17:49
Мне нравится этот ответ, потому что он на самом деле говорит вам, как решить проблему.

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

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

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

Ваш ответ на вопрос