Число с плавающей точкой в ​​JavaScript (IEEE 754) - PullRequest
2 голосов
/ 21 марта 2019

enter image description here

Если я правильно понимаю, числа JavaScript всегда сохраняются как числа с плавающей запятой двойной точности в соответствии с международным стандартом IEEE 754. Что означает, что он использует 52 бита для дробного значения. Но на рисунке выше, кажется, что 0,57 в двоичном формате использует 54 бита.

Другое дело (если я правильно понимаю), 0.55 в двоичном коде также является повторяющимся числом. Но почему 0.55 + 1 = 1.55 (без потерь) и 0.57 + 1 = 1.5699999999999998

4

Ответы [ 3 ]

3 голосов
/ 21 марта 2019

toString(2) печатает строку до последней ненулевой цифры.

1,57 имеет битовое представление, отличное от 1 + 0,57 (но не невозможно получить результат 1,57),
, но 1 + 0,55 вдвоичный код равен 1,55, как вы можете видеть в фрагменте ниже:

console.log(1.57)
console.log(1.57.toString(2))
console.log((1+.57).toString(2))
console.log("1.32 + 0.25 = ",1.32 + .25)
console.log((1.32 + .25).toString(2))
console.log(1.55)
console.log(1.55.toString(2))
console.log((1+.55).toString(2))

Помните, что компьютер выполняет операции с двоичными числами, 1.57 или 1.55 - это просто читабельный вывод

3 голосов
/ 22 марта 2019

Что означает, что он использует 52 бита для значащей дроби и. Но на рисунке выше, кажется, что 0,57 в двоичном коде использует 54 бита.

Тип JavaScript Number, который по сути представляет собой базовый 64-разрядный двоичный код с плавающей точкой IEEE 754, имеет 53-разрядное значение. 52 бита кодируются в поле «трейлинг значимости». Старший бит кодируется через поле экспоненты (поле экспоненты 1-2046 означает, что старший бит равен единице, поле экспоненты 0 означает, что старший бит равен нулю, а поле экспоненты 2047 используется для бесконечности или NaN).

Значение, которое вы видите для .57 имеет 53 значащих бита. Ведущий «0» производится операцией toString; это не является частью кодировки числа.

Но почему 0,55 + 1 = 1,55 (без потерь) и 0,57 + 1 = 1,5699999999999998.

Когда JavaScript форматирует некоторые Number x для отображения со своими правилами по умолчанию, эти правила говорят, что нужно создать самую короткую десятичную цифру (в ее значащих цифрах, не считая декорации, такие как ведущий «0»). ) при преобразовании обратно в формат Number получается x . Цели этого правила включают: (а) всегда обеспечивать, чтобы дисплей однозначно определял, какое именно значение Number было исходным значением, и (б) не использовать больше цифр, чем необходимо для выполнения (а).

Таким образом, если вы начнете с десятичного числа, такого как .57, и преобразуете его в Number, вы получите некоторое значение x , которое является результатом преобразования, необходимого для округления до представимого числа в формате Number. Затем, когда x отформатирован для отображения, вы получите исходное число, потому что правило, согласно которому создается самое короткое число, которое преобразуется обратно в x , естественно создает число, с которого вы начали.

(Но то, что x не точно представляет 0,57. Ближайшие double к 0,57 немного ниже его; см. десятичное и двоичное 64 его представления на IEEE double калькулятор ).

С другой стороны, когда вы выполняете какую-либо операцию, такую ​​как .57 + 1, вы выполняете некоторую арифметику, которая производит число y , которое не начиналось как простая десятичная цифра. Таким образом, при форматировании такого числа для отображения правило может потребовать использования большего количества цифр. Другими словами. Когда вы добавляете .57 и 1, результат в формате Number будет отличаться от того, который вы получили от 1.57. Таким образом, чтобы отформатировать результат .57 + 1, JavaScript должен использовать больше цифр, чтобы отличить это число от числа, которое вы получаете от 1.57 - они разные и должны отображаться по-разному.


Если бы 0.57 было точно представлено как double, результат предварительного округления суммы был бы точно 1.57, поэтому 1 + 0.57 округлилось бы до того же double, что и 1.57.

Но это не тот случай, это на самом деле 1 + nearest_double(0.57) =
1.569999999999999951150186916493 (предварительное округление, а не double), которое округляется до
1.56999999999999984012788445398. Эти десятичные представления чисел имеют гораздо больше цифр, чем нам нужно, чтобы различать 1ulp (единицу в последнем месте) значимого и даже максимальную ошибку округления 0,5 ulp.

1.57 округляет до ~ 1.57000000000000006217248937901 , так что это не вариант для печати результата 1 + 0.57. Десятичная строка должна отличать число от соседних двоичных значений64.


Просто так получается, что округление, которое происходит в .55 + 1, дает то же число, которое получается при преобразовании 1.55 в Number, поэтому при отображении результата .55 + 1 получается «1,55».

1 голос
/ 21 марта 2019

Number.prototype.toString примерно реализует следующий раздел спецификации ES262:

7.1.12.1 NumberToString (м)

пусть n, k и s - целые числа, такие что k ≥ 1, 10 ** k-1 ≤ s <10 ** k, </p>

Числовое значение для s × 10 ** н-к, м

и k как можно меньше.

Поэтому toString просто оценивает значение, но не возвращает точные сохраненные байты.

То, что вы видите в консоли, также не является точным представлением.

...