Empaquetar los pesos ternarios {-1, 0, 1} de modelos de lenguaje como BitNet b1.58 obliga a buscar un esquema que aproveche al máximo cada byte. El artículo analiza por qué un bloque de 5 trits (dígitos ternarios) cabe de forma casi óptima en 8 bits: 5 trits generan 243 combinaciones (3⁵), mientras que un byte admite 256 valores, lo que arroja una eficiencia del 99,06 % frente al ideal teórico de log(3)/log(2) ≈ 1,585 bits por trit. El método resultante trabaja a 1,6 bits por trit y solo necesita multiplicaciones, sin recurrir a divisiones ni módulos en la fase de desempaquetado.
El truco consiste en tratar los cinco trits como un número en base 3 y mapearlo a un byte mediante una división entera con redondeo hacia arriba: b = ((b * 256) + 242) // 243. Para recuperarlos, basta multiplicar el byte por 3 y leer los dos bits más significativos del resultado de 10 bits; el resto se enmascara con 0xFF y el proceso se repite cinco veces. Esta aritmética basada en punto fijo evita el operador módulo, que no suele estar disponible en instrucciones SIMD, y permite vectorizar el desempaquetado con AVX2 y ARM NEON.
El autor acompaña la explicación con código en Python y un programa en C que verifica los 243 valores posibles sin pérdida de información. La técnica ya se emplea en los tipos ternarios de llama.cpp para TriLMs y BitNet b1.58, según el pull request #8151 del repositorio ggml-org, lo que la convierte en una solución práctica para reducir el tamaño de los modelos sin sacrificar velocidad de inferencia.
