← Назад к статьям

Магия битов: как Go представляет бесконечность, NaN и единицу

M
miholeus
28 марта 2026 г. · 4 мин чтения

Когда 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, но и в том, как числа с плавающей точкой устроены на любой платформе.

Комментарии 0

Комментарии проходят модерацию
Загрузка комментариев...