Два в степени x
Вы узнаете
Как exp2 делит x на целую часть и дробь и почему её тесты доходят только до целой части.
Запись ещё не готова: она ждёт команд tri test и tri mutate plant из gHashTag/t27#7400, которые запись запускает, а до тех пор виджет ниже -- заглушка без запуска. Заголовок gft_exp2.t27 называет exp2 недостающим кирпичиком для softmax на GF-T. on_comb делит x на целую часть k и дробь f: k задаёт смещение, k + 40, а pow2_frac превращает f в мантиссу. Заголовок обещает ошибку не больше 1 ULP, измеренную в прототипе, но все 4 теста подают целые входы, 0, 1.0, -1.0 и 2.0, поэтому f равна 0, и pow2_frac возвращает 0 при любых коэффициентах. Браузер пропускает все 4 теста, потому что его исполнитель пока не знает assert_eq; нативный t27c запускает все 4, все проходят, ни один не пустой. Запись теряет минус у k для отрицательного x, и падает ровно один тест, em1: 2^-1 выходит как 2.0, а не 0.5. Каждый байт в записи напечатала команда; инсценирован только набор текста.
Попробуйте
Найдите в записи изменённую строку и значение, которого ждёт em1; затем найдите в спеке pow2_frac и выясните, что она возвращает, когда f равна 0.

Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.
specs/ternary/gft_exp2.t27
module GftExp2;
// #1764 + GF-T: a GF-T exp2 primitive -- 2^x for a signed GF-T16 input x, result
// a positive GF-T16. This is the missing building block for a GF-T softmax
// (softmax(l) = 2^l / sum 2^l, in base-2). Approach, all integer / hardware-shaped:
// 1. |x| -> Q16.16 fixed-point (positive shifts only -> no signed-shift ambiguity).
// 2. split into integer k = floor(x) and fraction f in [0,1) (sign-aware floor).
// 3. 2^x = 2^k * 2^f. 2^k is an exact GF-T offset (k+40); 2^f-1 gives the mantissa
// via a Q16 quartic with rounded Horner shifts (<=1 ULP vs the exact mantissa).
// 4. clamp/saturate the offset to [1,80]; sign is always + (2^x > 0).
// Accuracy: <=1 ULP vs the true round_to_GFT(2^x) over the logit range (measured in
// the prototype). Bit-exact to the committed integer oracle over the vector file.
//
// Input: x signed GF-T16 (u32). Output: 2^x as a positive GF-T16 (u32).
// mant(f) = round(512*(2^(f/65536) - 1)), f in [0,65536), via a Q16 quartic with
// rounded Horner shifts. Coefficients fit + local-searched to <=1 ULP.
fn pow2_frac(f: i32) -> i32 {
var p : i32 = 6;
p = (((p * f) + 32768) >> 16) + 29;
p = (((p * f) + 32768) >> 16) + 123;
p = (((p * f) + 32768) >> 16) + 354;
p = ((p * f) + 32768) >> 16;
if (p > 511) { p = 511; }
return p;
}
fn on_comb(x: u32) -> u32 {
if (x == 0) { return 20480; } // 2^0 = +1.0
var neg : i32 = 0;
if ((x >> 16) == 1) { neg = 1; } // sign bit set -> x is negative
var off_in : i32 = ((x >> 9) & 127) as i32;
var mant_in : i32 = (x & 511) as i32;
// absurd-magnitude guard (keeps intermediates in i32; |x| >= 2^8 -> sat/underflow).
if (off_in >= 48) {
if (neg == 1) { return 512; } // underflow -> smallest normal (2^-39)
return ((80 << 9) | 511) as u32; // saturate -> max magnitude
}
// |x| in Q16.16 (positive shifts only).
var num : i32 = 512 + mant_in;
var sh : i32 = off_in - 33;
var mq : i32 = num;
if (sh >= 0) { mq = num << sh; } else { mq = num >> (0 - sh); }
var ki : i32 = mq >> 16;
var ff : i32 = mq & 65535;
// sign-aware floor: k = floor(x), f = x - k in [0,1).
var k : i32 = ki;
var f : i32 = ff;
if (neg == 1) {
if (ff == 0) { k = 0 - ki; f = 0; }
else { k = 0 - (ki + 1); f = 65536 - ff; }
}
var mant : i32 = pow2_frac(f);
var off : i32 = k + 40;
if (off < 1) { return 512; } // underflow -> smallest normal
if (off > 80) { return ((80 << 9) | 511) as u32; }
return ((off << 9) | mant) as u32;
}
// 2^0 = 1.0 (0x5000).
test e0 { assert_eq(on_comb(0), 20480); }
// 2^(+1.0) = 2.0 : x=+1.0=0x5000|... actually x encodes the exponent value 1.0 -> 20480.
test e1 { assert_eq(on_comb(20480), 20992); } // 2.0 = offset41
// 2^(-1.0) = 0.5 = offset39 (0x4e00).
test em1 { assert_eq(on_comb(86016), 19968); } // 0x4e00
// 2^(+2.0) = 4.0 = offset42 (0x5400).
test e2 { assert_eq(on_comb(20992), 21504); } // 0x5400
endmodule
Все уроки
Модуль 1 · Лаборатория: наши исследования
Свой формат чисел, честная таблица результатов и таблицы модели, перемноженные на плате.
Модуль 2 · ИИ-числа: блок MX
Как ИИ-чипы хранят веса в нескольких битах: один общий масштаб на блок, сам байт масштаба и что один выброс делает с соседями.
Модуль 3 · Тернарные веса
Веса, которые бывают только минус масштаб, ноль или плюс масштаб, пять правил, которые должен пройти тернарный алфавит, и прогон тестов, который ничего не проверил.
Модуль 4 · Тернарный формат Ternary Network Float
Правило, которое компилятор проверяет до запуска любого теста, и 17-битное число с плавающей точкой, порядок которого — четыре сбалансированных трита.
Модуль 5 · Арифметика чисел со знаком
Умножить два числа со знаком, сложить их, когда знаки разные, и сделать то и другое сразу в умножении с накоплением.
Модуль 6 · Части нейрона
ReLU с изломом в нуле, степень двойки для softmax и argmax, который называет ответ.
Модуль 7 · Обучение на ошибке
Потеря, которая оценивает неверную догадку в битах, один шаг, который сдвигает вес против градиента, и скрытый слой, который нужен XOR.
Модуль 8 · BitNet: тернарные сети
Порог, который сжимает сумму обратно до трёх значений, один нейрон, который становится другой функцией при смене весов, и нейрон, который читает входы по 27 тритов за раз.
Модуль 9 · Тернарный MAC как чип
Скалярное произведение 27 тритов как провода без регистра, та же сумма, которую регистр копит на каждом такте, и небольшая целая сеть в конце курса.