Большие числа ошибочно округлены в 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

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

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

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

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

Ответ 1

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

Ответ 2

Вы переполняете емкость типа номера 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 бит значимости (но формат настолько умный, что он получает 53 бит точности из этих 52 бит). Как используется показатель экспоненты, сложный (описанный здесь), но в очень неопределенных терминах, если мы добавим его к экспонента, значение знака удваивается, так как показатель степени используется для степеней 2 (опять же, предостережение там, оно не прямое, там есть умность).

Итак, посмотрим на значение 9007199254740991 (aka, 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. И так далее.

Ответ 4

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

ceil(lb 714341252076979033) = 60

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

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

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

Ответ 5

Ваша проблема в том, что ваш номер требует большей точности, чем javascript.

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

Ответ 6

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