Number Formats
Number formats are the bit-level encodings used to store and compute tensor values, such as BF16, FP8, INT8, INT4, MXFP4, and NVFP4. They decide how many bytes move through memory, how much range and precision each value keeps, and whether hardware can execute the resulting operations natively.
Large models spend much of inference moving weights, activations, and KV cache through memory rather than doing useful arithmetic. Full-precision formats preserve numerical comfort but make every token read more bytes. Reduced formats exist because fewer transported bits can raise throughput, fit longer contexts, or host more concurrent requests, provided the kernel does not immediately expand everything back into a larger format and lose the saving.
The central question is where the exponent went. BF16 keeps the wide exponent range of FP32 but trims precision, so it is forgiving for varied activations. FP8 still has per-value exponent bits, with variants trading range against mantissa precision. INT8 and INT4 remove per-value exponents and represent values as integer codes under a shared scale. MXFP4 and NVFP4 use tiny element payloads plus fine-grained block scaling, so range partly lives in metadata.
Lower precision is not a free compression switch. Shared scales mean outliers can waste codes for the rest of a group, while smaller groups improve accuracy but add scale overhead. Some workloads become limited by conversion, dequantisation, launch latency, or underfilled tensor cores instead of memory traffic. It is commonly misunderstood that INT4 or FP4 automatically means faster inference. The honest answer depends on hardware support, fused kernels, calibration, and model sensitivity.
Engineers meet number formats in quantised GEMM kernels, KV-cache settings, serving engines, and hardware compatibility tables. Hopper-class systems commonly make FP8 attractive, while Blackwell adds native NVFP4-style execution paths. Frameworks may expose AWQ, GPTQ, FP8 KV cache, bitsandbytes-style loading, or FP4 storage, but the important check is whether computation stays in an efficient native path or silently round-trips through BF16.
Common questions
- Is FP8 the same idea as INT8?
- No. FP8 stores a sign, exponent, and mantissa in each value, so every element carries some of its own range information. INT8 stores an integer code and relies on an external scale, often per tensor, channel, or group. That makes INT8 simple and compact, but outliers in a shared group can reduce effective precision.
- Why can BF16 be more stable than smaller formats?
- BF16 keeps the same exponent budget as FP32, so very small and very large intermediate values are less likely to underflow or overflow. It sacrifices mantissa precision instead. That trade is often acceptable in transformer inference, where dynamic range across logits, residual paths, and attention scores can matter more than many extra fractional bits.
- What makes MXFP4 and NVFP4 different from ordinary INT4?
- INT4 is usually an integer code interpreted through a shared scale. Microscaling FP4 formats use very small float-like element codes plus a fine-grained scale for a block of values. The block scale acts like shared exponent metadata, while the element code keeps a tiny amount of local structure. Native hardware support determines whether this is compute-efficient.
- When does a lower-bit format fail to improve speed?
- It fails when memory traffic is not the bottleneck, or when the implementation pays too much for scales and conversions. If a kernel reads compact weights, expands them into BF16 in memory, and only then multiplies, much of the benefit disappears. Small batches, short contexts, and unsupported hardware paths can also hide or reverse the gain.