Что означает, что он использует 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».