Візуалізатор чисел IEEE-754
Подивіться, як десяткове число насправді зберігається як число IEEE-754 — біт знаку, біти експоненти й мантиси — і чому такі числа, як 0.1, не зберігаються точно.
Результат
Кожне число з рухомою комою, яке зберігає комп'ютер, — число JavaScript, float у C, float у Python — кодується однаково за стандартом IEEE 754: один біт знаку, блок бітів експоненти та блок бітів мантиси (дробової частини). Одинарна точність (32 біти) використовує 1 біт знаку, 8 бітів експоненти і 23 біти мантиси; подвійна точність (64 біти) — формат, на якому базується числовий тип за замовчуванням майже в кожній мові програмування, — використовує 1 біт знаку, 11 бітів експоненти і 52 біти мантиси. Цей інструмент бере будь-яке введене вами десяткове число, кодує його в одній із двох розрядностей за допомогою вбудованого в браузер кодувальника DataView/ArrayBuffer — того самого механізму, на якому насправді працює ваш код, — і розфарбовує кожен біт відповідно до того, якій ділянці він належить.
Саме кодування складається з трьох кроків, і цей інструмент проходить через усі три, а не просто показує фінальні біти. Спочатку нормалізація: число переписується як 1,мантиса × 2^експонента, з рівно однією ненульовою цифрою перед комою (для чисел, замалих для цього, натомість застосовується субнормальна обробка без неявної провідної одиниці). Далі — зміщення експоненти: оскільки поле експоненти має зберігати і додатні, і від'ємні показники як число без знаку, перед записом у двійковому вигляді додається фіксоване зміщення (127 для одинарної точності, 1023 для подвійної). Нарешті, дробова частина мантиси розгортається побітово класичним методом «помножити на два, взяти цілу частину як наступний біт, зберегти залишок» — це повторюється, доки поле мантиси не заповниться, після чого залишкові біти визначають, округлити результат угору чи вниз (округлення до парного — типова поведінка IEEE-754).
Саме тому таке звичайне число, як 0.1, не зберігається точно. У двійковій системі 0.1 — це періодичний дріб (0.0001100110011… і так до нескінченності), тож жодна скінченна мантиса не може зберегти його точно — округлення відбувається неминуче. Закодуйте 0.1 як 32-бітне число з рухомою комою і зчитайте біти назад — ви отримаєте не 0.1, а точно 0.100000001490116119384765625. У подвійної точності на 29 бітів мантиси більше, тож похибка округлення значно менша, але вона теж не нульова — 0.1 як 64-бітне число подвійної точності — це точно 0.1000000000000000055511151231257827021181583404541015625. Цей інструмент обчислює те саме точне збережене значення за допомогою арифметики великих цілих чисел (без наближень із плаваючою комою), тож різниця між тим, що ви ввели, і тим, що насправді зберігає апаратура, ніколи не приховується і не апроксимується.
Особливі значення отримують точні, зарезервовані бітові шаблони замість звичайного кодування: нуль і від'ємний нуль мають нульові біти експоненти й мантиси (відрізняється лише біт знаку), ±Infinity має всі одиничні біти експоненти з нульовою мантисою, а NaN — усі одиничні біти експоненти з принаймні одним встановленим бітом мантиси. Усе тут працює локально у вашому браузері — введені вами значення нікуди не надсилаються, — тож це безпечне місце, щоб напрацювати справжню інтуїцію щодо помилок із плаваючою комою: чому `0.1 + 0.2 !== 0.3` майже в кожній мові програмування, чому текстури float32 у графічному коді втрачають точність, якої не втратив би float64, і чому у фінансовому коді загалом варто уникати двійкової плаваючої коми.