TGViewer
8BitJS 8BitJS @eightbitjs · 162 subscribers
Post #9 286
​​HeapNumber в V8. Как хранятся числа вне Smi. Теория часть 1

Продолжим рассматривать способы хранения чисел во время выполнения кода внутри V8. Когда движок сталкивается с числом, выходящим за пределы диапазона Smi, он создаёт HeapNumber — полноценный объект в куче.

Что такое HeapNumber


HeapNumber — это 64-битное число с плавающей точкой, завёрнутое («упакованное») в объект на куче. Такое представление часто называют boxed double, поскольку значение double не может храниться напрямую в регистрах или указателях и оборачивается (boxing) в отдельную структуру с типовой информацией (Map) и полем для самого значения.

Когда возникает HeapNumber

Любые JavaScript Number, выходящие за пределы Smi-диапазона: содержащие дробную часть, ±Infinity, NaN, −0, а также любые результаты арифметических операций, приводящие к потере точности, переполнению или переходу в формат double (например, деление двух целых чисел с нецелым результатом).

Внутреннее устройство HeapNumber

Обратимся к исходникам:
- src/objects/heap-number.h
- src/objects/primitive-heap-object.h

Сам класс HeapNumber наследуется от PrimitiveHeapObject. Чтобы отделить «примитивы-объекты» (числа, BigInt, WasmNumber, String, но не Smi) от обычных JavaScript-объектов (таких как JSArray, JSFunction и т.д.), в V8 ввели промежуточный абстрактный класс PrimitiveHeapObject.

Он не содержит собственных полей или методов — его задача типизации. Это маркер, который позволяет компилятору и внутренним шаблонам V8 статически проверять, что конкретный класс представляет примитивное значение, не содержит тегированных ссылок (tagged pointers).

В V8 64-битное значение внутри double обрабатывается как два 32-битных слова. Это позволяет повысить эффективность операций, особенно на архитектурах с ограниченной поддержкой 64-битных инструкций или в оптимизированных путях компиляции.

| high word | low word |
|(32 бита) |(32 бита) |
| ----------------- | ---------------|
| S (1 бит) | M-high (20 бит)|
| E (11 бит) | |
| M-low (32 бита) | |

Обозначения:
- S — знаковый бит (1 = отрицательное значение)
- E — экспонента (11 бит)
- M — мантисса (52 бита в сумме)

Это деление соответствует стандарту IEEE-754 и отражает, как double хранится в памяти. Такое представление упрощает доступ к частям числа для анализа, компиляции и оптимизаций.

Помним и про порядок байт (endianness). Чаще используется Little-endian (x86-64, ARM64): нижнее слово хранится по меньшему адресу. Но есть и Big-endian

Внутреннее устройство констант

Для работы с HeapNumber V8 определяет несколько констант и масок:

Маска для извлечения знакового бита из старшего 32-битного слова. Если бит установлен, число отрицательное:

static const uint32_t kSignMask = 0x80000000u;  
// 10000000 00000000 00000000 00000000


Маска для извлечения всех 11 бит экспоненты из старшего слова:

static const uint32_t kExponentMask = 0x7ff00000u;
// 01111111 11110000 00000000 00000000


Маска для извлечения верхних 20 бит мантиссы из старшего слова (остальные 32 бита мантиссы находятся в младшем слове):

static const uint32_t kMantissaMask = 0xfffffu;
// 00000000 00001111 11111111 11111111


К интересному можно отнести проверку на Infinity и NaN

static const int kInfinityOrNanExponent =          
(kExponentMask >> kExponentShift) - kExponentBias;


Проверка на Infinity и NaN важна, потому что эти значения имеют одинаковую структуру на уровне IEEE-754: у них устанавливаются все биты экспоненты (11 битов равны 1), но различаются значения мантиссы:
- Если экспонента максимальна, а мантисса = 0, то это ±Infinity.
- Если экспонента максимальна, а мантисса ≠ 0 — это NaN.

---

#V8 #JavaScript #HeapNumber #IEEE754 #JSразбор
  • 🔥 5
More from @eightbitjs
  1. Jul 21, 2026​​Ускоряем O(N + T), не меняя Big O. Часть 5. Зачем здесь WebAssembly После реализации бак…
  2. Jul 18, 2026​​Итоги CodeRun Summer: 15 задач и 636 попыток решения Финал CodeRun Summer Challenge выгл…
  3. Jul 17, 2026​​Ускоряем O(N + T), не меняя Big O. Часть 4. Кэш, бакеты и упаковка событий В предыдущей…
  4. Jul 16, 2026​​Ускоряем O(N + T), не меняя Big O. Часть 3. Как оставить четыре байта и не сломать ответ…
  5. Jul 15, 2026​​Ускоряем O(N + T), не меняя Big O. Часть 2. Четыре байта, которые могут изменить результ…
  6. Jul 14, 2026​​Ускоряем O(N + T), не меняя Big O. Часть 1. Разностный массив — это только начало На про…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →