INT4 Quantization › 4-Bit Number Formats
Language AI HandbookAll chapters INT4 Quantization › 4-Bit Number FormatsOne section · Sections in this chapter4 / 11 ↓
Reading tools Definitions & inline links
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
Not all 4-bit formats are the same. Your choice of quantization format significantly affects model quality, and the design decisions are not obvious. The standard INT4 format, while simple and well-understood, may not be optimal for neural network weights. Several specialized 4-bit formats have been developed to better match the statistical properties of weight distributions, each with its own trade-offs between representational efficiency and computational convenience. The key question underlying all format choices is: given that we have exactly 16 representable values, where should we place them to minimize reconstruction error for typical neural network weights?
Think of the format choice as choosing where to put the markers on a ruler. A uniform ruler spaces marks evenly, which is optimal when measurements are uniformly distributed. But if most measurements cluster between 0 and 10 cm on a meter-long ruler, you would want finer marks in that region and coarser marks elsewhere. The NF4 format does exactly this for neural network weights: it places more marks where weights are dense (near zero) and fewer marks in the sparse tails.
The choice of format interacts with the group-wise quantization approach we just discussed. Group-wise quantization handles the outlier problem by adapting the scale locally, but it still uses a uniform grid within each group. A better number format, by contrast, uses a non-uniform grid that is optimized globally for the expected weight distribution. These two techniques are complementary rather than redundant: group-wise quantization handles local variation in weight magnitude, while a format like NF4 handles the global shape of the weight distribution.
Understanding these format choices also requires appreciating what "optimal" means in this context. From an information-theoretic perspective, the ideal quantization grid for data drawn from distribution places quantization levels at the quantile boundaries of . This keeps each level is used equally often. This maximum-entropy criterion minimizes the average reconstruction error when the data distribution matches the assumed one. This is exactly the design principle behind NF4, and it explains why NF4 consistently outperforms uniform INT4 for normally distributed weights.
Standard INT4
Standard INT4 uses 4 bits to represent integers in a fixed range. The simplicity of this format makes it computationally efficient and easy to implement:
- Unsigned INT4: Values 0 to 15, representing 16 non-negative integers
- Signed INT4: Values -8 to 7 (or -7 to 7 for symmetric quantization around zero)
The quantization levels in standard INT4 are uniformly spaced across this range, meaning adjacent levels differ by exactly the same amount regardless of where they fall in the range. For neural network weights that follow a roughly Gaussian distribution, this uniform spacing wastes representational capacity. The problem is one of mismatch between the format and the data. Many quantization levels fall in the tails of the distribution where few weights exist, while the dense center region near zero has too few levels to capture the subtle variations among the many weights clustered there. If 80% of your weights fall in the middle third of your quantization range, you are spending only 5 of your 16 levels on 80% of the data, and 11 levels on the remaining 20%. This is a poor allocation.
NF4 (Normal Float 4)
NF4, introduced alongside QLoRA, represents a fundamentally different approach to 4-bit representation. Rather than accepting uniform spacing as a given, NF4 is specifically designed for normally distributed data. Instead of uniform spacing, NF4 places quantization levels such that each level represents an equal probability mass under a standard normal distribution.
NF4 chooses its 16 quantization levels so that when weights are normally distributed, each level is equally likely to be used. This maximizes information entropy and minimizes expected quantization error for Gaussian-distributed weights. The mathematical foundation for this approach comes from information theory: by so each quantization level is equally probable, we extract maximum information from our limited 4-bit budget. In information-theoretic terms, NF4 achieves the optimal rate-distortion trade-off for a Gaussian source with 4-bit quantization.
The NF4 quantization levels are computed through a principled mathematical procedure. The core idea is to divide a standard normal distribution into equal-probability regions, choose representative values for those regions, normalize the resulting codebook to the interval , and include an exact zero. For group-normalized Gaussian weights, this produces substantially more balanced level usage than a uniform INT4 grid and reduces average reconstruction error.
To use NF4 for weights that are not necessarily drawn from a unit normal distribution, we first normalize the weight group to have a maximum absolute value of 1 (or fit within the range [-1, 1]), then apply the NF4 lookup. The per-group scale factor handles this normalization. This is why NF4 and group-wise quantization work together smoothly: the group scale normalizes the local weight distribution so that the NF4 grid, designed for a standard normal, provides a good fit.
from scipy import stats
def compute_nf4_levels():
"""
Return the canonical 16-value NF4 codebook used by QLoRA/bitsandbytes.
"""
return np.array(
[
-1.00000000,
-0.69619280,
-0.52507305,
-0.39491749,
-0.28444138,
-0.18477343,
-0.09105004,
0.00000000,
0.07958030,
0.16093020,
0.24611230,
0.33791524,
0.44070983,
0.56261700,
0.72295684,
1.00000000,
]
)
nf4_levels = compute_nf4_levels()
# Model realistic group-wise normalization: each 64-weight group is divided
# by its own maximum absolute value before applying a 4-bit codebook.
format_rng = np.random.default_rng(42)
format_groups = format_rng.standard_normal((160, 64))
test_weights = (
format_groups / np.max(np.abs(format_groups), axis=1, keepdims=True)
).ravel()NF4 Quantization Levels (16 values): ---------------------------------------- Level 0: -1.0000 Level 1: -0.6962 Level 2: -0.5251 Level 3: -0.3949 Level 4: -0.2844 Level 5: -0.1848 Level 6: -0.0911 Level 7: +0.0000 Level 8: +0.0796 Level 9: +0.1609 Level 10: +0.2461 Level 11: +0.3379 Level 12: +0.4407 Level 13: +0.5626 Level 14: +0.7230 Level 15: +1.0000
Notice how the NF4 levels are denser near zero, where most weights concentrate, and sparser in the tails where weights are rare. This non-uniform spacing is the key innovation: by allocating more quantization levels to the regions where data is abundant, NF4 reduces average quantization error compared to uniform spacing. The levels near zero differ by small amounts, letting fine discrimination among the many similar small weights. The levels in the tails differ by larger amounts, but this matters less because few weights fall there. The smallest gap between adjacent NF4 levels is in the center; the largest gap is at the extremes. This is precisely inverted from what you would want for a uniform distribution, but perfectly matched for a Gaussian.
# Code demonstration only - plotting moved to visualization block
pass

The visualization confirms that NF4 concentrates resolution where the data is, minimizing the expected error for Gaussian-distributed weights compared to the uniform grid of INT4. This distribution-aware approach to level placement is what makes NF4 particularly well-suited to neural network weight quantization, where approximate normality is a reasonable assumption for most layers. This assumption does not always hold perfectly: some layers, particularly in certain architectures, can have more bimodal or heavy-tailed weight distributions. In those cases, NF4's advantage over INT4 is smaller. But across the broad sweep of transformer layers, normality is a good approximation, and NF4 reliably outperforms uniform INT4.
FP4 (4-Bit Floating Point)
Another approach is to use a floating-point representation with 4 bits. This format attempts to bring the dynamic range advantages of floating-point to the extremely constrained 4-bit budget. FP4 typically uses:
- 1 sign bit, determining whether the value is positive or negative
- 2 exponent bits, controlling the magnitude or scale of the value
- 1 mantissa bit, giving a single binary digit of precision within each exponent range
This format has a dynamic range similar to floating point and can represent a wide range of values using the exponent. However, the single mantissa bit means each exponent range has only 2 possible values (the implicit leading 1 and either 0 or 1 in the mantissa position). This extreme coarseness limits the practical utility of FP4 for many applications.
def generate_fp4_values():
"""
Generate all possible FP4 values.
Format: 1 sign bit, 2 exponent bits, 1 mantissa bit
Uses E2M1 format with bias of 1
"""
values = []
# Exponent bias
bias = 1
for sign in [0, 1]:
for exp in range(4): # 2 bits = 0-3
for mantissa in range(2): # 1 bit = 0-1
if exp == 0:
# Subnormal numbers
value = (mantissa / 2) * (2 ** (1 - bias))
else:
# Normal numbers
value = (1 + mantissa / 2) * (2 ** (exp - bias))
if sign == 1:
value = -value
values.append(value)
return sorted(set(values))
fp4_values = generate_fp4_values()FP4 (E2M1) Quantization Values: ---------------------------------------- Value 0: -6.0000 Value 1: -4.0000 Value 2: -3.0000 Value 3: -2.0000 Value 4: -1.5000 Value 5: -1.0000 Value 6: -0.5000 Value 7: +0.0000 Value 8: +0.5000 Value 9: +1.0000 Value 10: +1.5000 Value 11: +2.0000 Value 12: +3.0000 Value 13: +4.0000 Value 14: +6.0000
FP4's values are concentrated near zero but with exponentially increasing gaps as values get larger, following the characteristic pattern of floating-point representations. This structure can be useful for weight distributions with heavy tails, where the ability to represent large outliers without sacrificing too much precision near zero is valuable. However, the extreme coarseness introduced by having only a single mantissa bit limits practical utility for most neural network applications, where the fine distinctions among small weights are often critically important. The FP4 grid is essentially a power-of-2 grid: values jump from 0.5 to 1.0 to 2.0 to 4.0, which leaves enormous gaps relative to the typical weight magnitudes in most layers.
Comparing 4-Bit Formats
Let's quantize the same weights using different 4-bit formats and compare the reconstruction error. This empirical comparison will reveal how format choice affects quantization quality for normally distributed data:
def quantize_to_nearest_level(weights, levels):
"""Quantize each weight to the nearest available level."""
weights_flat = weights.flatten()
levels = np.array(levels)
# Find nearest level for each weight
distances = np.abs(weights_flat[:, np.newaxis] - levels[np.newaxis, :])
nearest_idx = np.argmin(distances, axis=1)
quantized = levels[nearest_idx]
return quantized.reshape(weights.shape)
# Quantize with different formats
int4_levels_symmetric = np.linspace(-1, 1, 16)
fp4_levels_normalized = np.array(fp4_values) / np.max(np.abs(fp4_values))
q_int4 = quantize_to_nearest_level(test_weights, int4_levels_symmetric)
q_nf4 = quantize_to_nearest_level(test_weights, nf4_levels)
q_fp4 = quantize_to_nearest_level(test_weights, fp4_levels_normalized)
# Calculate metrics
mse_int4 = np.mean((test_weights - q_int4) ** 2)
mse_nf4 = np.mean((test_weights - q_nf4) ** 2)
mse_fp4 = np.mean((test_weights - q_fp4) ** 2)Reconstruction Error for Normally Distributed Weights: -------------------------------------------------- INT4 (uniform): MSE = 0.001482 NF4 (normal-opt): MSE = 0.001278 FP4 (E2M1): MSE = 0.001724 -------------------------------------------------- NF4 vs INT4 improvement: 13.8%


For normally distributed weights, NF4 provides lower quantization error than uniform INT4 because its levels are better matched to this distribution. This improvement is not coincidental but follows directly from the information-theoretic principles underlying NF4's design: by matching the quantization grid to the data distribution, we reduce expected reconstruction error. The level-utilization chart makes the benefit tangible: uniform INT4 concentrates assignments in its central bins, while NF4 uses the available codebook more evenly. Finite group size and absmax normalization mean the utilization is not perfectly uniform, but the allocation is substantially better balanced.