Javascript вычисляет неправильный результат на большом количестве [дубликат]

Не уверен в специфике Java API, но общий способ сделать это - добавить групповую проверку на запрос / привязку.

41
задан T.J. Crowder 14 July 2016 в 11:19
поделиться

6 ответов

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

50
ответ дан simeg 16 August 2018 в 10:48
поделиться

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

ceil(lb 714341252076979033) = 60

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

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

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

4
ответ дан Christoph 16 August 2018 в 10:48
поделиться

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

1
ответ дан Community 16 August 2018 в 10:48
поделиться

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

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

2
ответ дан James Skemp 16 August 2018 в 10:48
поделиться

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

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

Beyond 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. И так далее.

40
ответ дан T.J. Crowder 16 August 2018 в 10:48
поделиться
  • 1
    Мне нравится этот ответ, потому что он на самом деле говорит вам, как решить проблему. – jsh 3 April 2013 в 16:49

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

Подробнее см. В этом блоге: http://www.exploringbinary.com/print-precision-of-floating-point-integers-varies-too

7
ответ дан thorn 16 August 2018 в 10:48
поделиться
  • 1
    Спасибо за ссылку на мою статью, но это только объясняет половину проблемы - ПЕЧАТЬ значения, округленного внутри. Даже если javascript позволяет печатать все это, все равно будет ошибкой - это будет ближайшая представляемая ценность двойной точности, как объясняется другими ниже. – Rick Regan 4 September 2009 в 23:04
Другие вопросы по тегам:

Похожие вопросы: