От золотого сечения до троичности Брусенцова: одно семейство числовых форматов от 2 до 1024 бит

от автора

От золотого сечения до троичности Брусенцова: одно семейство числовых форматов от 2 до 1024 бит — и что я в нём реально измерил

Памяти Николая Петровича Брусенцова (7 февраля 1925 — 4 декабря 2014).

Эту статью я посвящаю памяти человека, который поверил в троичность тогда, когда весь мир уже выбрал двойку. Главный конструктор «Сетуни» — первой в мире электронной троичной ЭВМ, участник Великой Отечественной войны (радист-разведчик с 1943 года), кандидат технических наук, он с 1953 по 2014 год работал в МГУ и до последних дней заведовал лабораторией троичной информатики (ВМК МГУ; Брусенцов, Рувики). В феврале 2025 года ему исполнилось бы 100 лет (ВМК МГУ, к 100-летию).

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

Все мы привыкли, что чисел с плавающей точкой «бывает несколько»: float32, float16, bfloat16, в последние пару лет — FP8 и совсем экзотика вроде MXFP4. Каждый из них — это компромисс между диапазоном и точностью, который кто-то однажды зафиксировал руками: столько-то бит на экспоненту, столько-то на мантиссу.

А что если не выбирать сплит руками, а вывести его из одной математической константы — золотого сечения? Так появился GoldenFloat: семейство форматов, где разбиение «экспонента : мантисса» на каждой ступеньке задаётся одним и тем же законом, привязанным к тождеству φ² + φ⁻² = 3. А на самом нижнем, 2-битном пределе этого семейства неожиданно оказывается старая знакомая — балансная троичность {−, 0, +}, та самая, на которой в 1958 году в МГУ построили легендарную «Сетунь».

Эта статья — про обе вещи сразу и про их связь. Сначала про φ-лестницу форматов и единственное, что в ней измерено end-to-end. Потом про «Сетунь», Брусенцова и красоту балансной троичности. И в конце — про то, как троичность вернулась в эпоху больших языковых моделей (BitNet) и как она садится в φ-семейство как его нижняя ступень. Честно, со всеми статус-метками: что измерено, что только спецификация, а что вообще не моё изобретение.

Сразу честная рамка, потому что без неё статья превратилась бы в рекламу: end-to-end я проверил пока только один формат семейства — GF16. Всё остальное — это спецификация и, местами, RTL. Оптимальность всего семейства — открытый вопрос.

Препринт по GoldenFloat: arXiv:2606.05017.

Оглавление

Откуда вообще берётся «сплит» в float

Любое двоичное число с плавающей точкой шириной N бит — это три поля: знак S, экспонента E и мантисса M, причём S + E + M = N. Например, классический float16 — это 1+5+10, а bfloat16 — 1+8+7. Разница только в том, сколько бит отдали под диапазон (экспонента), а сколько под точность (мантисса).

Эти сплиты исторически выбирались под задачу. bfloat16 «украл» три бита у мантиссы и отдал их экспоненте, чтобы покрыть тот же диапазон, что и float32 — для обучения нейросетей диапазон оказался важнее точности. То есть никакого «закона» тут нет: есть инженерные решения, каждое со своей историей.

Вопрос, с которого всё началось: а можно ли выбрать сплит не вкусовым решением, а единым правилом, одинаковым для всех ширин — от пары бит до тысячи?

Почему именно золотое сечение

φ-якорь семейства: тождество, числа Люка, константа-семя

φ-якорь семейства: тождество, числа Люка, константа-семя

Золотое сечение φ = (1+√5)/2 ≈ 1,618 знаменито своим «самоподобием»: φ² = φ + 1. Из него же следует менее известное, но очень удобное тождество:

φ² + φ⁻² = 3

Это не нумерология и не магия — это элементарно проверяемое равенство (подставьте φ и посчитайте). Именно оно — якорь всей конструкции. В терминах чисел Люка оно записывается как L₂ = 3, а в «железе» превращается в конкретную константу-семя dot4 = 0x47C0, которая зашита в Verilog как 16'h47C0.

Идея сплита: выбирать соотношение числа бит экспоненты к числу бит мантиссы так, чтобы оно стремилось к 1/φ ≈ 0,618. Нормативная формула для ступеньки шириной N выглядит так:

phi  = (1 + 5 ** 0.5) / 2          # 1.6180339887...e    = round((N - 1) / phi**2)     # бит экспонентыm    = N - 1 - e                   # бит мантиссыbias = 2 ** (e - 1) - 1            # смещение экспоненты

Здесь N-1 — это все биты, кроме знакового. Один и тот же код порождает весь «лестничный» ряд форматов, не подбирая ничего руками.

Один закон на всю лестницу форматов

Один закон порождает всю лестницу форматов

Один закон порождает всю лестницу форматов

Если прогнать формулу по ширинам, получается семейство, которое я называю лестницей GF. Вот несколько характерных ступенек и то, насколько их сплит близок к идеальному 1/φ:

Формат

Сплит (S+E+M)

Бит

E/M

Близость к 1/φ

Статус

GF4

1+1+2

4

0,500

0,118

спека + RTL

GF8

1+3+4

8

0,750

0,132

спека + RTL

GF16

1+6+9

16

0,667

0,049

проверен e2e

GF32

1+12+19

32

0,632

0,014

спека + RTL

GF64

1+24+39

64

0,615

0,003

спека + RTL

GF256

1+97+158

256

0,614

0,004

спека + RTL

GF1024

1+391+632

1024

0,619

0,0006

только спека

Что тут важно и честно отметить: «близость к 1/φ» — это не показатель качества формата на реальных данных. Это просто метрика того, насколько аккуратно сплит лёг на целочисленную сетку бит. Чем шире формат, тем плотнее сетка и тем точнее можно попасть в идеал — поэтому у GF1024 близость лучшая, но это совершенно не значит, что GF1024 где-то «лучше всех». Это значит ровно одно: правило применимо на всём диапазоне ширин.

Аккуратная формулировка, которую я готов защищать (и она помечена как открытая гипотеза, а не доказанный факт):

Мне не известно другое опубликованное семейство float-форматов, у которого сплит E:M по всей лестнице от 2 до 256 бит порождался бы одним замкнутым правилом.

Это утверждение про методологию, а не про производительность. И формулировка именно «мне не известно», а не «единственное в мире» — потому что доказать отсутствие чего-либо нельзя.

Lucas-точные аккумуляторы: где φ ведёт себя как целое

Есть один по-настоящему приятный бонус золотого якоря. Степени φ²ⁿ + φ⁻²ⁿ — это всегда целые числа (это числа Люка с чётным индексом). То есть когда вы накапливаете суммы определённого вида, результат можно держать в точной целочисленной арифметике, без накопления ошибки округления.

Это свойство я проверил формально: целочисленный аккумулятор Lucas-EII прогнан для n = 1 … 256 с точностью 500 знаков и доказан в Coq. Для обычного float такое «бесплатное» отсутствие дрейфа округления — редкость, и именно тут золотое сечение даёт не эстетику, а инженерную пользу.

Что измерено на GF16 (и почему именно он)

GF16: эталон, измеренный замер, честная рамка

GF16: эталон, измеренный замер, честная рамка

GF16 — единственная ступенька, доведённая до конца: Rust-референс, Verilog-реализация и тестбенч на FPGA Artix-7, 35 из 35 тестов проходят, рабочая частота 323 МГц. Это «замороженный» якорь семейства — именно его сплит 1+6+9 служит эталоном, к которому привязана нормативная формула.

Теперь измерения. В сравнении на обучении байтового языкового моделирования (метрика val_bpb, меньше — лучше) картина такая:

Формат

val_bpb

Честное чтение

float32

2,5414

полноточностный базлайн

float16

2,5501

лучший 16-битный в этом прогоне

GF16

2,5725

+0,031 к f32; обходит bfloat16

bfloat16

2,6135

худший из 16-битной тройки

Что это значит на человеческом языке: GF16 играет в одной лиге с float16 и измеримо обходит bfloat16 на этой задаче. Но float16 его всё-таки слегка опережает. Это ограниченный эмпирический результат на одной задаче, а не «GF16 лучше всех».

И сразу честная рамка, чтобы никто не принял это за больше, чем оно есть. End-to-end измерен пока только GF16; остальные ступени — это спецификация и, где-то, RTL, без бенчмарков. Красивый сплит из 1/φ не означает оптимальности — известна работа Daubechies et al. (Golden Ratio Encoder), где φ не единственное «хорошее» основание; реальный плюс скромнее — «безмультипликаторность» (из φ² = φ + 1 множитель усиления схлопывается в единицу). Сопутствующий чип-каталог Corona — submission-ready дизайн (RTL + GDS + формальные доказательства + cocotb), проверенный в симуляции, а не изготовленный кристалл: проверено формально и в sim, кремний — впереди. Всё это я держу под явными статус-метками (Verified / Empirical fit / Open conjecture).

А теперь — самая красивая часть. У этой φ-лестницы есть не только верх (GF1024), но и низ. И на самом низу, на 2-битном пределе, обнаруживается то, что построили в железе ещё 68 лет назад.

Сетунь: машина, которой не дали жить

В 1958 году в вычислительном центре МГУ собрали машину, которая по меркам тогдашней (и сегодняшней) индустрии была еретической: она считала не в двоичном коде, а в троичном. Машина называлась «Сетунь», её главным конструктором был Николай Петрович Брусенцов, а идейным вдохновителем — академик С. Л. Соболев. Это была первая в мире электронная троичная ЭВМ — и, как часто бывает с красивыми идеями, она же оказалась почти единственной.

Сетунь 1958, трит и изящество балансной троичности

Сетунь 1958, трит и изящество балансной троичности

История начинается в начале 1956 года, когда в МГУ под началом Соболева взялись за создание небольшой ЭВМ — простой в освоении, надёжной и дешёвой. Год ушёл на изучение существующих машин, и по итогам команда приняла нестандартное решение: строить не на двоичном, а на троичном коде (Брусенцов, Рамиль Альварес, SoRuCom 2006).

К концу 1958 года «Сетунь» была собрана и начала работать; к концу 1959-го готова система программирования, а к апрелю 1960-го машина прошла межведомственные испытания (там же). Несколько сухих, но красноречивых цифр из той же статьи Брусенцова:

  • всего 24 одноадресные команды — минималистичный набор;

  • оперативная память — 162 девятитритные ячейки (3 страницы по 54);

  • регистры на 18 и 9 тритов, индексный регистр на 5 тритов;

  • около 2 тысяч элементов в машине, цена элемента — 3 рубля 50 копеек;

  • продавалась всего за 27 500 рублей.

И — что особенно впечатляет — надёжность. За первый год эксплуатации заменили всего три дефектных элемента; экспериментальный образец в вычислительном центре МГУ проработал «в абсолютно рабочем состоянии семнадцать лет непрерывной работы» (там же). Серийные машины работали в климате «от Одессы и Ашхабада до Якутска и Красноярска».

Серийное производство шло на Казанском заводе математических машин по решению Совета Министров СССР. Но, по словам Брусенцова, чиновникам из профильного ведомства троичная машина «по неизвестным причинам не понравилась», и к 1965 году производство остановили (там же). Между 1967 и 1969 годами появился преемник — «Сетунь 70» с двухстековой архитектурой, работавшая трайтами по 6 тритов и поддерживавшая идеи структурного программирования Дейкстры. Но широкой серии уже не случилось.

Что такое балансная троичность и почему она красива

Двоичный бит хранит одно из двух значений: 0 или 1. Трит хранит одно из трёх. Но «Сетунь» использовала не «несимметричную» троичность с цифрами {0, 1, 2}, а балансную (симметричную) — с цифрами {−1, 0, +1} (часто записывают как , 0, +).

Число записывается так же позиционно, как и обычно, только вес i-й позиции равен 3ⁱ, а цифры берутся из {−1, 0, +1}. Например (значения из статьи Брусенцова): десятичное 13 — это + + +, 7 — это + − +, 6 — это + − 0, а −6 — это − + 0 (Брусенцов, SoRuCom 2006).

У балансной троичности есть несколько свойств, которые ценили и Брусенцов, и до него — Клод Шеннон (отметивший преимущества системы ещё в 1950 году), и позже Дональд Кнут, назвавший её «возможно, изящнейшей» системой счисления (Брусенцов, SoRuCom 2006; Ternary computer, Wikipedia):

  • Знак — это просто старший ненулевой трит. Отдельный «знаковый бит» не нужен, знак числа определяется естественно.

  • Смена знака — инверсия разрядов. Каждый + становится и наоборот. Никакого дополнительного кода, как в двоичном.

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

  • Единая операция сдвига и единообразное кодирование чисел.

Есть и теоретико-информационный аргумент, который любят повторять: самым «экономным» по плотности хранения было бы основание e ≈ 2,718; из целых оснований к нему ближе всего тройка, а не двойка (Исследование элементов троичной логики, Молодой учёный). То есть троичность — не каприз, а попытка попасть в математически выгодную точку.

Важная честная оговорка: «экономнее по основанию» не значит «быстрее и дешевле в кремнии прямо сейчас». Реальный мир выбрал двойку по совокупности инженерных и экономических причин — об этом дальше.

Упущенный шанс: почему победил двоичный код

Если троичность так красива, почему мир считает в нулях и единицах? Коротко — не из-за математики, а из-за инженерии и инерции экосистемы:

  • Двоичная элементная база (транзистор как переключатель «вкл/выкл») оказалась проще, дешевле и быстрее в массовом производстве. Стабильно различать два уровня напряжения легче, чем три.

  • Вокруг двойки выросла вся экосистема: память, логика, стандарты, компиляторы, целые поколения инженеров.

  • «Сетунь», при всех её достоинствах, не получила административной поддержки — и красивая ветка эволюции просто заглохла.

Это классическая история про то, как «достаточно хорошее и повсеместное» побеждает «изящное, но одинокое». Троичность не была неправильной — она была несвоевременной.

Возвращение троичности: BitNet и веса в LLM

Возвращение троичности: веса LLM, безмультипликаторность, золотой 2-битный предел

Возвращение троичности: веса LLM, безмультипликаторность, золотой 2-битный предел

А теперь — поворот. В эпоху огромных языковых моделей главным дефицитом стали память и энергия: веса моделей измеряются миллиардами чисел, и хранить каждое в 16 или 32 битах дорого. Возникла волна квантизации — представлять веса всё более грубо, лишь бы модель не разваливалась.

И на пределе этой волны индустрия пришла ровно к троичности. Модели семейства BitNet (Microsoft Research) используют веса, принимающие фактически три значения — {−1, 0, +1} — что даёт знаменитые «1,58 бита на вес» (log₂ 3 ≈ 1,585). Это ровно балансная троичность Брусенцова, только теперь она живёт не в ферритовых сердечниках «Сетуни», а в матрицах весов нейросети.

Смысл тот же, что шестьдесят лет назад: три состояния {−, 0, +} оказываются удачным компромиссом. Ноль позволяет «выключить» связь (разреженность), а ±1 несут знак без отдельного бита. Умножение на такой вес — это вообще не умножение, а выбор «прибавить / ничего / вычесть». Брусенцов бы оценил.

Нижняя ступень: GFTernary и TF3 (честно)

Теперь — обещанная связь между φ-лестницей и троичностью. Если у GoldenFloat есть верх (GF1024), то у него есть и самый низ — 2-битный предел. И этот предел — троичный. Но здесь я обязан быть предельно честным насчёт статуса.

В каталоге есть два разных троичных объекта, которые легко спутать:

  • GFTernary — 2-битный формат, значения которого буквально {−φ, 0, +φ} (где φ — золотое сечение). По сути это балансная троичность Брусенцова, у которой ненулевая величина равна не единице, а φ. Это и есть нижняя ступень φ-семейства: тот же закон, доведённый до двух бит. Статус: спецификация + референс-реализация на Rust и Zig. Это не серийный чип.

  • TF3 — 8-битный контейнер для хранения троичных весов нейросети (геометрия 1+3+4, как у GF8) с метаданными. Это именно контейнер хранения, а не 2-битный формат. Статус: только спецификация.

И ещё одна честная деталь про «железо». В сопутствующем чипе-каталоге Corona есть декодер формата bitnet — то есть троичные представления учтены на уровне дизайна. Но Corona — это submission-ready дизайн (RTL + GDS + формальные доказательства + cocotb-тесты), проверенный в симуляции и формально. Изготовленного и протестированного на столе кристалла нет. Поэтому никаких «доказано на кремнии» — корректная формулировка: проверено в симуляции и формально, кремний впереди.

Чего я НЕ утверждаю, и это важно:

  • Я не изобретал троичную логику — это Брусенцов и «Сетунь», а в современном виде — BitNet от Microsoft Research. Мой вклад скромнее: троичность естественно ложится в φ-семейство как его 2-битный предел, с ненулевой величиной φ вместо единицы.

  • В моих измерениях end-to-end проверен пока только 16-битный GF16 (см. таблицу выше). Троичные ступеньки семейства — это спецификация, а не измеренный результат.

То есть связь честная и неслучайная: одна и та же идея — «три состояния {−, 0, +}» — проходит через «Сетунь», через BitNet и через 2-битный предел моего φ-семейства. Но превращать это в громкие заявления я не буду; статус каждого кирпичика указан явно.

Как это потрогать руками

Препринт со всеми деталями и таблицами лежит на arXiv:2606.05017. Нормативная формула сплита приведена выше — её достаточно, чтобы воспроизвести всю лестницу форматов в десяток строк на любом языке. Соседние форматы (FP8, bf16, posit, takum и другие) я держу в открытом каталоге как союзников и контрпримеры, а не как «поверженных конкурентов» — в частности, takum (Hunhold, 2024) специально лежит рядом как честный контрпример к гипотезе о «широте как преимуществе».

Выводы

  • Сплит «экспонента : мантисса» можно не подбирать руками, а вывести из одного закона — стремления E/M к 1/φ, с якорем φ² + φ⁻² = 3. Получается единое семейство форматов на всём диапазоне ширин.

  • Золотой якорь даёт реальный инженерный бонус — Lucas-точные целочисленные аккумуляторы без дрейфа округления (проверено в Coq).

  • Из всего семейства end-to-end проверен только GF16: паритет с float16, измеримо лучше bfloat16 — но это один результат на одной задаче, не универсальная победа.

  • Внизу той же лестницы лежит троичность. «Сетунь» (1958, МГУ, Брусенцов) — первая в мире электронная троичная ЭВМ: 24 команды, ~2000 элементов, поразительная надёжность — и закрытая к 1965 году не по техническим причинам.

  • Балансная троичность {−1, 0, +1} математически изящна (знак — старший разряд, смена знака — инверсия, округление — отбрасывание младших). Победил двоичный код — из-за простоты элементной базы и экосистемы, а не из-за «неправильности» троичности.

  • В эпоху LLM троичность вернулась: BitNet b1.58 (Microsoft Research) — по сути та же балансная троичность Брусенцова в весах нейросети.

  • В моём φ-семействе троичность присутствует как GFTernary ({−φ, 0, +φ}, спека + Rust/Zig) и TF3 (контейнер, только спека); Corona — дизайн в симуляции, не чип. Никаких «доказано на кремнии».

Если интересно, в следующий раз могу подробно разобрать Lucas-точные аккумуляторы — с кодом и тем, как именно целочисленность φ²ⁿ + φ⁻²ⁿ убирает накопление ошибки, — и чем умножение на троичный вес дешевле обычного.

Открыт к сотрудничеству с AI-лабораториями и командами

Если вы из AI-лаборатории, чип-вендора или ML-команды и вам интересно попробовать GoldenFloat (в первую очередь проверенный GF16, а также лестницу форматов, Lucas-точные аккумуляторы и троичную нижнюю ступень в духе BitNet) на своих задачах — напишите мне. Я открыт к совместной работе в любом из форматов:

  • прогнать GF16 на вашем железе или в вашем training/inference-стеке и честно сравнить с float16 / bfloat16 / FP8 на ваших данных;

  • честно сравнить троичные/низкоразрядные представления (BitNet-веса, GFTernary как 2-битный предел) на ваших задачах;

  • помочь с интеграцией форматов в ваши кернелы и инструментарий (есть Rust-референс, Verilog/FPGA-реализация и открытый каталог совместимости с бит-точными конформанс-векторами и Corona-дизайн как оракул);

  • совместно довести до измерений те ступеньки лестницы, которые пока существуют только как спецификация;

  • спонсируемое исследование или совместный технический отчёт.

Честная рамка остаётся прежней: я не обещаю «формат лучше всех». Я предлагаю воспроизводимую методологию и один измеренный результат (GF16), а дальше — смотрим на ваших задачах вместе и публикуем то, что реально получилось, со всеми статус-метками.

Пишите на admin@t27.ai — расскажите, какой у вас стек и какую задачу хотите проверить. Общение асинхронное, на английском или русском.


Об авторе. Dmitrii Vasilev, Trinity S³AI. ORCID 0009-0008-4294-6159. Препринт по GoldenFloat: arXiv:2606.05017. Связь: admin@t27.ai

ссылка на оригинал статьи https://habr.com/ru/articles/1050924/