Магия битов: как Go представляет бесконечность, NaN и единицу
Когда Go возвращает +Inf, -Inf или NaN из функций пакета math — за этим стоит не математика, а аккуратно уложенные биты. Разбираемся, как устроены специальные константы в исходниках Go и почему они выглядят именно так.
Формат IEEE 754: краткий ликбез
Все числа float64 хранятся в 64-битном формате IEEE 754. Каждый бит имеет строго определённую роль:
[S][EEEEEEEEEEE][FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF]
1 бит знак 11 бит экспоненты 52 бита мантиссы
- S — знаковый бит (0 = положительное, 1 = отрицательное)
- E — экспонента, хранится со смещением (bias = 1023)
- F — дробная часть мантиссы (целая
1.подразумевается неявно)
В исходниках Go эти параметры вынесены в именованные константы:
const (
mask = 0x7FF
shift = 64 - 11 - 1 // = 52
bias = 1023
signMask = 1 << 63
fracMask = 1<<shift - 1
)
uvinf и uvneginf — положительная и отрицательная бесконечность
uvinf = 0x7FF0000000000000
uvneginf = 0xFFF0000000000000
По стандарту IEEE 754 число является бесконечностью, когда все биты экспоненты равны 1, а все биты мантиссы равны 0.
Разберём uvinf по битам:
0x7FF0000000000000
= 0111 1111 1111 0000 ... 0000
^
S=0 (положительное)
^^^^^^^^^^^
экспонента = 0x7FF = 11111111111 (все единицы)
мантисса = все нули
uvneginf отличается только знаковым битом:
0xFFF0000000000000
= 1111 1111 1111 0000 ... 0000
^
S=1 (отрицательное)
Функция Inf() просто выбирает нужную константу и переинтерпретирует её байты как float64 через Float64frombits — без какой-либо математики:
func Inf(sign int) float64 {
var v uint64
if sign >= 0 {
v = uvinf
} else {
v = uvneginf
}
return Float64frombits(v)
}
uvnan — Not a Number
uvnan = 0x7FF8000000000001
NaN по IEEE 754 — это любое число, где экспонента состоит из всех единиц (как у бесконечности), но мантисса не равна нулю. Именно ненулевая мантисса отличает NaN от ∞.
0x7FF8000000000001
= 0111 1111 1111 1000 0000 ... 0000 0001
^
S=0
^^^^^^^^^^^
экспонента = 11111111111 (все единицы, как у ∞)
^
бит 51 мантиссы = 1 ← тип NaN
^
бит 0 мантиссы = 1 ← мантисса ненулевая
IEEE 754 различает два вида NaN:
| Тип | Бит 51 мантиссы | Поведение |
|---|---|---|
| Quiet NaN (qNaN) | 1 | тихо распространяется в вычислениях |
| Signaling NaN (sNaN) | 0 | вызывает аппаратное исключение |
0x7FF8... — это quiet NaN: старший бит мантиссы равен 1, поэтому он безопасно возвращается из функций, не вызывая исключений. Младший бит 0x1 явно подчёркивает, что мантисса ненулевая, то есть это точно не бесконечность.
Итого в битах:
+∞: 0 | 11111111111 | 0000...0000 (мантисса = 0)
NaN: 0 | 11111111111 | 1000...0001 (мантисса ≠ 0)
uvone — единица
uvone = 0x3FF0000000000000
Это битовое представление числа 1.0.
0x3FF0000000000000
= 0011 1111 1111 0000 ... 0000
^
S=0 (положительное)
^^^^^^^^^^^
экспонента = 0x3FF = 01111111111 = 1023
мантисса = все нули
Экспонента 1023 при смещении bias = 1023 даёт реальную степень 0:
реальная экспонента = хранимая - bias = 1023 - 1023 = 0
По формуле нормализованного числа:
(-1)^S × 1.мантисса × 2^(экспонента - bias)
= 1 × 1.0 × 2^0
= 1.0
Мантисса хранит только дробную часть — целая 1. подразумевается неявно (implicit leading bit). Все нули в мантиссе означают ровно 1.0, без какой-либо дробной части.
Константа используется в функциях вроде math.Frexp и math.Ldexp, где число нужно быстро собрать или разобрать на битовом уровне, минуя обычную арифметику.
Сводная таблица
| Константа | Hex | Значение | Экспонента | Мантисса |
|---|---|---|---|---|
uvinf |
0x7FF0000000000000 |
+∞ |
все 1 | все 0 |
uvneginf |
0xFFF0000000000000 |
-∞ |
все 1 | все 0 |
uvnan |
0x7FF8000000000001 |
NaN |
все 1 | ≠ 0 |
uvone |
0x3FF0000000000000 |
1.0 |
1023 | все 0 |
Вывод
Специальные значения float64 — это не магия математики, а конкретные битовые паттерны, зафиксированные стандартом IEEE 754. Go хранит их как uint64-константы и использует Float64frombits для zero-cost преобразования: ни одного вычисления, только переинтерпретация байтов. Понимание этих паттернов помогает разобраться не только в исходниках Go, но и в том, как числа с плавающей точкой устроены на любой платформе.