INT4 Quantization › 4-Bit Number Formats

Michael BrenndoerferJanuary 12, 202613 min read
Language AI HandbookAll chapters
Part 42 of 67Inference Optimization
Chapter 319INT4 Quantization › 4-Bit Number Formats
Reading section 4 of 11 · 4-Bit Number Formats · 13 minSection 4 of 11 · 13 min

Book contents

Section 4 of 11 · 13 min read

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 p(w)p(w) places quantization levels at the quantile boundaries of p(w)p(w). 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 Design Principle

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 [−1,1][-1, 1], 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.

In[20]:
Code
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()
Out[21]:
Console
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.

In[22]:
Code
# Code demonstration only - plotting moved to visualization block
pass
Out[23]:
Visualization
Density curve of group-normalized Gaussian weights overlaid with 16 coral vertical lines marking NF4 quantization levels, showing denser spacing near the peak and wider gaps toward the tails.
NF4 quantization levels overlaid on the density of Gaussian weights after 64-value group-wise absmax normalization. The 16 coral vertical lines concentrate resolution in the dense region near zero and space levels more widely in the sparse tails.
Out[24]:
Visualization
Two rows of tick marks from minus one to one showing INT4 levels evenly spaced and NF4 levels more tightly clustered near zero with wider gaps toward the extremes.
Spacing comparison between INT4 and NF4 quantization levels on the normalized interval. INT4 (blue, top row) uses uniform spacing. NF4 (coral, bottom row) clusters levels near zero and spaces them more widely at the extremes, matching group-normalized Gaussian weights.

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.

In[25]:
Code
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()
Out[26]:
Console
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:

In[27]:
Code
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)
Out[28]:
Console
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%
Out[29]:
Visualization
Bar chart comparing mean squared error for INT4, NF4, and FP4 on Gaussian weights normalized in groups of 64, with NF4 showing the lowest bar.
Mean squared error comparison across three 4-bit formats for Gaussian weights normalized in groups of 64. NF4 (green) achieves the lowest reconstruction error by matching level placement to the group-normalized distribution, outperforming uniform INT4 (blue) and normalized FP4 (orange).
Out[30]:
Visualization
Grouped bar chart of utilization percentages across 16 levels, with NF4 assignments more evenly distributed than INT4 and a dashed reference at 6.25 percent.
Quantization level utilization across all 16 levels for Gaussian weights normalized in groups of 64. NF4 (green) distributes assignments more evenly across the codebook, while uniform INT4 (blue) concentrates more heavily in central levels. The dashed line marks ideal 6.25% utilization.

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.