Как хранится float
IEEE 754 хранит число как знак × 1.дробная часть × 2экспонента, то есть в двоичной научной нотации. У 32-битного float 1 бит знака, 8 бит экспоненты и 23 бита дробной части; у 64-битного double — 1, 11 и 52. Экспонента хранится со смещением (127 или 1023), чтобы ей не нужен был собственный знаковый бит, а ведущая единица мантиссы не хранится вовсе, потому что у нормальных чисел она есть всегда.
| Знак | Экспонента | Дробная часть | Смещение | Десятичных цифр | |
|---|---|---|---|---|---|
| float32 (binary32) | 1 | 8 | 23 | 127 | ~7 |
| float64 (binary64) | 1 | 11 | 52 | 1023 | ~16 |
Почему 0.1 + 0.2 не равно 0.3
У 0.1 нет конечного двоичного разложения, как у 1/3 нет конечного десятичного. Ближайший float64 — это 0.1000000000000000055511151231257827…, и 0.2 с 0.3 тоже немного отличаются от точных. Сложение двух округлённых значений с повторным округлением даёт результат на одну единицу дальше от float, ближайшего к 0.3, поэтому сравнение не проходит. Введите выше 0.1 и посмотрите на строку «точное сохранённое значение», чтобы увидеть округление напрямую.
Специальные шаблоны
- Ноль: экспонента и дробная часть — все нули. Знаковый бит при этом учитывается, поэтому существует −0, равный +0 при сравнении.
- Субнормальное: экспонента — все нули, дробная часть ненулевая. Это крошечные значения ниже нормального диапазона, с пониженной точностью и без неявной ведущей единицы.
- Бесконечность: экспонента — все единицы, дробная часть 0. Возникает при переполнении и делении на ноль.
- NaN: экспонента — все единицы, дробная часть ненулевая. Возникает из 0/0, ∞−∞, sqrt(−1). NaN не равен ничему, включая самого себя.
Чтение битов вручную
- Разбейте шаблон на поля знака, экспоненты и дробной части.
- Вычтите смещение из экспоненты, чтобы получить реальную степень двойки.
- Поставьте «1.» перед битами дробной части (или «0.» для субнормальных) и умножьте на 2 в этой степени.
Для 0x40490FDB: знак 0, экспонента 10000000 = 128 → 128 − 127 = 1, дробная часть 1.1001001000011111101101 → 1.5707963 × 2 = 3.1415927, ближайший к π float32.
Вопросы
В чём разница между float и double?
Float (float32) использует 32 бита и даёт около 7 значащих десятичных цифр; double (float64) использует 64 бита и даёт около 16. Double — тип по умолчанию в большинстве языков (числа JavaScript, float в Python, double в C); float применяют там, где важны память или пропускная способность GPU.
Почему экспонента использует смещение, а не дополнительный код?
Со смещением float одного знака правильно сравниваются как обычные целые: больший битовый шаблон — большее число. Это делает сортировку и сравнение дешёвыми в аппаратуре.
Что такое машинный эпсилон?
Расстояние между 1.0 и следующим представимым float: 2⁻²³ ≈ 1,19 × 10⁻⁷ для float32 и 2⁻⁵² ≈ 2,22 × 10⁻¹⁶ для float64. Шаг растёт вместе с величиной числа: начиная с 2²⁴ float32 уже не может представить нечётные целые.
Какое наибольшее целое double хранит точно?
2⁵³ = 9 007 199 254 740 992. Выше не все последовательные целые представимы, поэтому в JavaScript есть Number.MAX_SAFE_INTEGER (2⁵³ − 1) и BigInt.
Почему мой язык печатает 0.30000000000000004?
Потому что это кратчайшая десятичная запись, однозначно определяющая double, полученный из 0.1 + 0.2. Печать корректна; от 0.3 отличается именно сохранённое значение.