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

Кортеж повышения был бы моим предпочтительным вариантом для обобщенной системы возврата больше чем одного значения от функции.

Возможный пример:

include "boost/tuple/tuple.hpp"

tuple <int,int> divide( int dividend,int divisor ) 

{
  return make_tuple(dividend / divisor,dividend % divisor )
}
55
задан Mamun 28 October 2018 в 13:26
поделиться

6 ответов

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

62
ответ дан 26 November 2019 в 17:46
поделиться

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

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

9
ответ дан 26 November 2019 в 17:46
поделиться

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

ceil(lb 714341252076979033) = 60

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

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

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

5
ответ дан 26 November 2019 в 17:46
поделиться

Вы переполняете емкость числового типа 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 -754, плавающий формат точки больше не может представлять каждое последовательное целое число. 9007199254740991 + 1 равно 9007199254740992 , но 9007199254740992 + 1 равно также 9007199254740992 , потому что 9007199254740993 не может быть представлен в формате. Следующим может быть 9007199254740994 . Тогда 9007199254740995 не может быть, но 9007199254740996 может.

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

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


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

Итак, давайте посмотрим на значение 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)

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

В конце концов, у нас заканчиваются значащие биты. снова и нужно увеличить показатель степени,

52
ответ дан 26 November 2019 в 17:46
поделиться

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

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

4
ответ дан 26 November 2019 в 17:46
поделиться

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

2
ответ дан 26 November 2019 в 17:46
поделиться
Другие вопросы по тегам:

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