Вторая статья про проект GoldenFloat. Первая была про φ-лестницу форматов и троичную «Сетунь» Брусенцова. Здесь — про инструмент, который из той работы вырос: один машинно-проверяемый каталог числовых форматов, где у каждого формата прописана разрядка битов, битовый отпечаток и метка зрелости — вплоть до той ступени, где формула ещё есть, а железа под ней уже нет.

Если вы хоть раз ловили расхождение точности между двумя реализациями одной сети, то знаете это чувство. Один и тот же matmul на двух устройствах даёт разные числа. И ты сидишь и гадаешь: это баг, или bf16 так округлил, или вообще на одном устройстве формат не тот, что ты думал. Беда в том, что две команды на разных концах конвейера меряют один результат разными линейками — и абсолютно корректное значение у одного читается как брак у другого.

Я решил сделать одну линейку с точными насечками. Для каждого формата — сколько бит уходит на знак, экспоненту и мантиссу, чему равно смещение, как кодируются inf, NaN и субнормали, где лежит общая реперная точка. Не текстом в README, а машинным источником истины: один файл, из которого автоматом генерируются Markdown, JSON, Python, Rust, C и RTL для кремния. Разойдётся спека с декодером — это ловит CI, а не человек на ревью в три часа ночи.

Препринт каталога: arXiv:2606.09686.

Обложка: Golden Ruler — каталог из 83 числовых форматов
Обложка: Golden Ruler — каталог из 83 числовых форматов

Оглавление

Зачем вообще каталог форматов

Лет десять назад в ML хватало float32 и float16. Дальше зоопарк начал разрастаться: bfloat16, TF32, два несовместимых FP8 (E4M3 и E5M2), FP6, FP4, микромасштабируемые MX-форматы с общей экспонентой, NF4 из мира QLoRA, posit’ы, takum’ы, логарифмические LNS. У каждого свой компромисс между диапазоном и точностью, свои правила округления, свои углы.

Проблема даже не в количестве, а в том, что спеки этих форматов растащены по статьям, стандартам и кодовым базам — и противоречат друг другу порой внутри одной библиотеки. Любимый пример: что делает FP8 E4M3 при переполнении на кодировании? У tt-metal и AMD — насыщается до 448.0, у JAX и TPU — уходит в NaN. Стандарт OCP MX разрешает оба. Пока вы не зафиксировали, какой вариант у вас, вопрос «совпадает ли результат» становится не проверяемым, а вкусовым.

Каталог отвечает на это одним файлом-спецификацией, из которого всё остальное выводится механически.

Что внутри: 83 формата в 13 кластерах

На сегодня живой источник истины (specs/numeric/formats_catalog.t27) держит 83 формата в 13 кластерах. Число я каждый раз перепроверяю на свежем клоне репозитория — почему именно так, скажу в разделе про ограничения. Разбивка:

Кластер

Кол-во

Что внутри

GoldenFloat

22

φ-семейство GF2…GF1024 + гибриды (GF+LNS, MXGF) (arXiv:2606.05017)

HistoricalVendor

10

IBM HFP, VAX (F/D/G/H), Cray, Microsoft MBF

PositUnumIII

8

Posit8…64, takum8…64 (Hunhold 2024)

IntegerFixed

8

INT4…INT128, Q-формат, BCD

MlLowPrecision

7

bfloat16, TF32, FP8 (E4M3/E5M2), FP6, FP4

Ieee754Binary

5

binary16/32/64/128/256

Theoretical

4

minifloat, Unum I/II, tapered FP

Lns

4

логарифмические LNS-8…64

CompressionTrick

4

block FP, shared-exponent, per-channel scale, stochastic rounding

Microscaling

3

MXFP8/6/4 (общая экспонента E8M0) (Rouhani 2023)

Ieee754Decimal

3

decimal32/64/128

ExtendedFloat

3

x87 FP80, double-double, quad-double

QuantTuned

2

NF4 (Dettmers 2023), AFP

Разрядность тянется от 2 бит (балансная троичность GFTernary, привет Брусенцову) до 1024 бит (GF1024, крайняя ступень φ-лестницы). У каждой строки кроме s/e/m/bias есть поле phi_distance — насколько разбиение формата близко к тому, что даёт золотое сечение через тождество φ² + φ⁻² = 3.

Раз уж речь зашла про слово «golden» — это обычный отраслевой термин, эталонное значение, с которым сравнивают результат, как эталонный метр в палате мер и весов. Им оперируют и в NVIDIA, и в Intel, и любой инженер на HDL. Каталог просто говорит на том же языке, а не выдумывает свой.

Метки зрелости

Дисциплина, на которой держится весь проект, — метка зрелости у каждого формата. Она говорит ровно одно: насколько эта строка проверена на самом деле.

Статус

Кол-во

Что означает

Verified

51

сверен с эталоном (стандарт или ml_dtypes)

Historical

12

исторический формат (IBM, VAX, Cray…), для полноты

Experimental

11

спека плюс частичная реализация, без полного покрытия

Open

9

только спецификация из правила, без RTL

Здесь же проходит граница, которая мне важнее красивых цифр: где правило e = round((N−1)/φ²) ещё работающее железо, а где оно уже гипотеза. GF16 доведён до кремния (FPGA 35/35 тестов, 323 МГц на Artix-7, кремниевый якорь). Под ступенями GF48…GF256 есть RTL, но кремния нет. GF512 и GF1024 — экстраполяция правила, у них в SSOT прямо стоит (extrapolation, no RTL), и я не делаю вид, что они проверены. Эту лестницу разбираю ниже.

Побочный эффект такой дисциплины: значение 0.1 в bfloat16 показано в каталоге с ненулевой ошибкой — потому что 0.1 в bf16 точно не представимо. Спрятать эту ошибку значило бы сломать саму линейку, ради которой всё затевалось.

Где правило перестаёт быть железом

φ-семейство держится на одном правиле: для ширины N бит экспонента e = round((N−1)/φ²), мантисса m = N − 1 − e, смещение bias = 2^(e−1) − 1. Правило короткое и применимо к любой ширине. Но «формат описан правилом» и «формат крутится в железе» — это разные вещи, и смешивать их я себе не позволяю.

Вот как лестница φ-ступеней выглядит в репозиториях прямо сейчас:

Уровень зрелости

Ступени

Что реально есть

Доведено до кремния

GF16

RTL + FPGA 35/35 @ 323 МГц + кремниевый якорь gf16_v2_mul.v

RTL + бенчмарк (Verified)

GF8, GF12, GF32, GF64

файлы _add.v / _mul.v + измерения

RTL написан, кремния нет (Open / Exp)

GF6, GF10, GF14, GF20, GF24, GF48, GF96, GF128, GF256

_add.v / _mul.v есть (сумматор GF256 ≈ 939 строк Verilog); тесты местами

Экстраполяция, RTL нет (Open)

GF512, GF1024

только спека; файлов gf512* / gf1024* в RTL нет

Тут вылезает неудобная вещь про GF1024. По метрике близости к φ это лучшая ступень лестницы (phi_distance ≈ 0.0006), и при этом она проверена меньше всех — ни одной строки Verilog. Красивая метрика не повышает уровень зрелости, и каталог это показывает, а не заметает.

RTL, который уже есть, — не для вида. Умножитель GF256 в начале июня пришлось переписать после реального бага: произведение мантисс выходило на 2 бита у́же положенного, и 1.0 × 1.0 давало 0.5. Поймали это только потому, что под форматом лежит железо, которое можно прогнать на тестах. Под GF512 и GF1024 такого железа пока нет — отсюда и честная пометка.

Якорь 0x47C0

У каталога есть одна общая реперная точка, и она растёт из того же тождества: φ² + φ⁻² = 3 = L₂, второе число Люка. Если посчитать dot4(1, 2, 3, 4) в GF16, бит-в-бит получается значение с отпечатком 0x47C0. Этот якорь проходит без изменений через все реализации — от JSON-векторов до RTL-декодеров на кремнии (проект Corona). Разъехалась эта одна точка — значит линейка где-то сбита, ищи где.

Это проверка на одинаковость, а не доказательство превосходства. Оговорка, которую держу жёстко: φ-семейство заслуживает место в каталоге широтой охвата и связностью тулчейна, а не тем, что каждая ступень кого-то побеждает. takum (Hunhold, 2024) — сильный контрпример, и он не спрятан, лежит в каталоге рядом со всеми.

Линейка, кластеры, якорь 0x47C0
Линейка, кластеры, якорь 0x47C0

Связь с IEEE P3109

Рабочая группа IEEE P3109 стандартизирует семейство 8-битных форматов для ML. В каталоге лежит cross-walk — одностраничная карта, сопоставляющая семейство P3109 binary8 (p1…p7) с индексами каталога. Это не заявление о соответствии стандарту, а справочник для разработчиков, который сама рабочая группа может проверить и поправить. Где есть расхождение — каталог уступает Lean-формализации FLoPS (arXiv:2602.15965).

Позиция тут сознательная: заходить не с «примите нас», а с проверяемой таблицей, которую можно сверить и опровергнуть.

Что это даёт на практике

Самое прямое применение — арбитр при расхождении точности. Когда один расчёт даёт разные числа на двух устройствах (классика: PCC проседает с 0.99 до 0.93 при другой блокировке редукции), бит-точный эталон формата механически отвечает, чей результат корректен, без споров на глаз.

Второе — кодоген под новое железо. Из одной спеки выводятся декодеры на нужном языке, включая RTL. Цепочка SSOT → кодоген → RTL → кремний целиком механическая, а CI-гейт rom_readback ловит любое расхождение между спекой и тем, что реально лежит в ПЗУ декодера на чипе.

Третье — общий словарь. Когда вы и собеседник из другой команды называете формат одинаково, с одной разрядкой и одним поведением на границах, исчезает целый класс споров в духе «а у нас не так».

Ограничения

Чтобы статья не съехала в рекламу, перечислю прямо:

  • Кремнием проверен пока только GF16. Для GF48…GF256 RTL написан, но не в кремнии; GF512 и GF1024 — экстраполяция правила без RTL. Самая близкая к φ ступень (GF1024) проверена меньше всех.

  • Кремний Corona — это законченный дизайн (RTL + GDS + формальная верификация + cocotb), а не отлитый чип. Проверено в симуляции и формально; кремний впереди.

  • φ-семейство не заявлено как «лучше всех». Место в каталоге — за широту и связность, не за победу на каждой ступени. takum лежит там же как сильный контрпример.

Каталог — это инструмент и дисциплина, а не финальное слово. Линейка стоит ровно столько, сколько стоят её насечки, — поэтому я и показываю, где они сейчас сбиты.


Источники и материалы:

Аффилиация: Trinity S³AI (независимый исследовательский коллектив). ORCID: 0009-0008-4294-6159. Контакт: admin@t27.ai (асинхронно).

Комментарии (4)


  1. DustCn
    24.06.2026 21:35

    Хотелось бы уточнить - обычный float/double на x86 - это GF32/GF64 в вашей статье или что то другое?


    1. B13nerd
      24.06.2026 21:35

      Это IEEE 754: single - binary32, double - binary64.


  1. wataru
    24.06.2026 21:35

    Ваши форматы - это лишь чуть-чуть измененное соотношение битности мантиссы/экспоненты. При чем никаких измерений о том, чем оно лучше и для каких задач нет.

    Каталог форматов - это одна табличка. Это трактат на 1-2 страницы. Делать из этого несколько статей - моветон.

    Алгоритмы вычислений с числами с плавающей точкой не имеют каких-то особых случаев в зависимости от битности (кроме возможно крайних случаев 1 битности, но это тривиальный неинтересный случай). Ну им безразницы складывать ли 7 битные мантиссы, или 9 битные. В железе просто добавляются полу-сумматоры какие-нибудь нужное количество раз. Схемы обобщаются на n бит тривиально.

    Как-то верифицировать эти вычисления для чуть других размеров полей - бессмысленная и бесполезная работа. Имеет смысл может быть проверять вашу реализацию с точки зрения, что вы там не набаговали при реализации, но это тупо отладка, и никакое не свойство формата.

    Вы бы еще придумали свой "брильянотвый int" размером 37 бит. Ведь такого нигде нет. Новое у уникальное. Нигде не опубликованное. А потом в поте лица верифицировали бы, что такие числа можно складывать и умножать.

    Что касается "закона" - это карго культ. Никакой пользы не несет, выбраны ли размеры мантиссы и экспоненты по кокнретному закону, или пальцем в небо, если этот закон тоже выбран пальцем в небо.

    Вся эта движуха просто пахнет курсовой студента, которому нужна актуальность и новизна и выдумывается что-то бесполезное, пишется раздутое описание, а потом усиленно не замечается слон в комнате - ведь смысла никакого в этом нет (кроме того, что студенту нужен зачет, но вслух это говорить нельзя).


    1. raoffonom Автор
      24.06.2026 21:35

      Спасибо за разбор — отвечу по пунктам, со ссылками на первоисточники, а не на собственные слова. Часть критики справедлива, и я это отмечу прямо. Но центральный технический тезис — «схемы обобщаются тривиально, а складываемость безразлична к содержимому» — расходится с тем, что реально происходит в индустрии прямо сейчас. Конкретика ниже.

      1. «Им безразлично, складывать 7-битные мантиссы или 9-битные. Схемы обобщаются на n бит тривиально».

      Для сложения двух нормальных чисел — почти да. Но ровно там, где «тривиально», и сидят баги. И это не моё мнение:

      • Округление субнормалей в умножителе зависит от ширины. Построчный разбор типовой реализации FPU на Verilog прямо говорит: «эти определения старшей и младшей части произведения неправильны для субнормальных чисел; для них нужно добавить хотя бы один ведущий ноль», и далее — «сколько ведущих бит мы добавляем и сколько отрезаем, зависит от значения экспоненты» (Building an FPU in Verilog: Rounding Subnormal Numbers). То есть формула, верная для нормального произведения, на субнормалях даёт неверный результат, и поправка — функция от exponent и числа значащих бит, а значит и от ширины формата.

      • Поддержку субнормалей в железе часто вообще выкидывают ради площади. Официальная документация AMD на DSP58: «когда DSP58 обнаруживает субнормальный операнд, он трактует его как ноль с сохранением знака… субнормальный выход flush-ится в ноль» (AMD Versal DSP Engine docs). У NVIDIA в fast-режиме субнормали тоже flush-ятся в ноль, в IEEE-режиме — нет (NVIDIA, Floating Point on NVIDIA GPU). То есть «формат тот же, ширина та же» — а поведение на краю разное в зависимости от того, реализованы субнормали или flush-to-zero. Где именно срабатывает FTZ-логика (до или после округления) — тоже расходится между реализациями (Stack Overflow, FLT_HAS_SUBNORM).

      Так что «обобщается тривиально» верно для учебного нормального случая и ломается на краях. А края — это ровно то, что меряет каталог.

      2. «Им безразлично, что складывать».

      Три контрпримера прямо из продакшена, не из моей головы.

      • Один формат — три разные конвенции округления. Для блочного масштаба E8M0 в MX: PyTorch округляет RNE, спецификация MX требует round-toward-zero, а CUDA 12.8 — round-toward-zero либо round-toward-+inf. Это дословно зафиксировано в трекере PyTorch: «rounding: RNE… note that this does not match the rounding to e8m0 in the MX spec (round towards zero), or the rounding to e8m0 in CUDA 12.8 (round towards zero or round towards positive infinity)» (pytorch/pytorch#146414). Один и тот же формат, три несовпадающих результата на трёх стеках.

      • Два несовместимых FP8 E4M3. У одного переполнение при кодировании насыщается до 448.0 (tt-metal, AMD), у другого уходит в NaN (исходный NVIDIA float8_e4m3fn, JAX). Стандарт OCP MX явно разрешает оба режима — «narrowing conversions support both saturate and non-saturate modes» — и относит к implementation-defined ещё и конверсию NaN в FP6/FP4/INT8, и даже кодирование −2.0 в INT8 (FPRox: OCP MX Scaling Formats). Биты те же, ширина та же, поведение на границе диаметрально разное.

      • И это не косметика — это рушит обучение. Kempner Institute (Harvard) обучил почти тысячу языковых моделей и показал, что MXFP8 E4M3 систематически вызывает нестабильность: значение клампится в «overflow region», если его модуль больше 87.5 % абсолютного максимума в 32-элементном блоке; для log-normal-распределённых весов layer-norm это значит, что все 32 веса в блоке схлопываются в одно число, и градиент начинает расти неконтролируемо перед взрывом лосса (Kempner Institute, 2025).

      Если «безразлично, что складывать» — объясните, почему один тензор на двух стеках даёт 448.0 против NaN, почему один блок округляется тремя способами, и почему Harvard’у пришлось обучить ~1000 моделей, чтобы поймать поведение края формата. Каталог фиксирует именно такие развилки: не «новый формат», а какое поведение у какой реализации на краю, с битовым якорем 0x47C0 для сверки.

      3. «Верифицировать вычисления для других размеров — бессмысленно. Это тупо отладка, а не свойство формата».

      Здесь вы фактически сформулировали мой же тезис, и я с ним согласен. Я нигде не утверждаю, что «складываемость» — свойство формата. Метка Verified в каталоге означает буквально «реализация сверена с эталоном бит-в-бит», то есть да — это про отладку и воспроизводимость, а не про абстрактную математику. Ценность не в том, что «числа можно складывать» (можно, спасибо), а в том, что под меткой Verified стоит прогоняемый тест, а под Open — честно стоит «только спека, RTL нет». Каталог честен ровно в том, что не выдаёт отладочную проверку за открытие. Распределение статусов так и записано: из 83 форматов Verified — меньшинство, остальное помечено Historical / Experimental / Open.

      4. «Закон выбора битов — карго-культ. Нет разницы, по закону или пальцем в небо».

      Сильное замечание, отвечу прямо: я НЕ утверждаю превосходства φ-разбиения. У φ-семейства в каталоге нет метрики «лучше всех» — takum Хунхольда лежит там же как контрпример, не спрятан. Более того, литература прямо показывает, что универсально лучшего формата нет: в той же работе по takum posit слегка выигрывает на 8 и 16 битах, takum достигает паритета на 32 и обходит дальше, а по целочисленному представлению IEEE 754 обходит posit (Hunhold, arXiv:2412.20273). Выбор битов — это инженерный trade-off под диапазон и распределение, а не превосходство и не магия.

      Единственное операционное отличие замкнутого правила от ручной таблицы — воспроизводимость кодогена: одно правило e = round((N−1)/φ²) порождает всю лестницу от 2 до 1024 бит без сшивки «ручной низ + формула для верха». Это не «точнее», это «однороднее по происхождению, поэтому проще механически генерировать и проверять декодеры». И тут уместно вспомнить: сам IEEE 754 даёт формулу ширины экспоненты w = round(4·log₂k) − 13 только как ненормативную заметку, и она не работает для 16 и 32 бит — их разбиение задано вручную (IEEE 754 §3.6, разбор на Stack Overflow). Так что «формула против ручной таблицы» — это ровно тот компромисс, который уже сделал и сам стандарт; я просто провёл одно правило через всю лестницу и пометил, где оно проверено кремнием, а где нет.

      5. «Каталог — это одна табличка, трактат на 1-2 страницы. Делать из этого несколько статей — моветон».

      Частично соглашусь: сам перечень — это таблица. Но статья не про таблицу. Она про то, что из одного файла-источника механически генерируются декодеры на Markdown/JSON/Python/Rust/C/RTL, и CI-гейт ловит расхождение спеки с тем, что реально лежит в ПЗУ декодера на кремнии. Это исполняемая спецификация-арбитр, а не описание. Код открыт и проверяем: Rust-ядро кодека GF16 — ffi/src/lib.rs, сгенерированный каталог — gen/numeric/formats_catalog.rs. Если две статьи — перебор, скажите, какую конкретно часть схлопнуть; я не держусь за объём.

      6. «Брильянтовый int на 37 бит» и «пахнет курсовой студента».

      Принято как удар, отвечу делом, а не обидой. Разница между «придумал формат и верифицировал, что числа складываются» и тем, что есть, — вот в чём: баг в умножителе GF256 (1.0 × 1.0 давало 0.5, потому что произведение мантисс выходило на 2 бита уже положенного) был найден и исправлен именно потому, что под форматом лежит RTL, который гоняется на тестах. Курсовая обычно заканчивается там, где у меня началась отладка железа. И «слон в комнате», которого я якобы не замечаю, в статье назван прямым текстом в разделе ограничений: кремнием проверен только GF16 (FPGA, тестбенч 35/35, 323 МГц на Artix-7); GF512/GF1024 — экстраполяция без единой строки Verilog; φ-семейство не заявлено как «лучше всех». Я этого слона не прячу — он стоит в отдельном разделе с заголовком «Ограничения».

      Что из конкретики кажется лишним — уберу по пунктам, без обид. За критику спасибо: она по делу там, где про объём и про отсутствие сравнительных бенчмарков по всей лестнице — их и правда нет, измерен end-to-end только GF16, остальное честно помечено статусами.