Comparison table

Quantisation formats and what they cost

FP32 to INT4, NF4, GGUF k-quants, AWQ and GPTQ on one page, with bytes per parameter, memory for 7 B and 70 B models, reported accuracy impact and hardware support.

NVIDIAOpen sourceLast verified 13 Nov 2026· llama.cpp 1.0, bitsandbytes 0.50, vLLM 0.33; verified 13 November 2026

This page is for anyone deciding how to fit a model onto the hardware they have. Each row gives the storage cost, the memory for the weights of a 7 B and a 70 B dense model, what the format’s own paper or maintainers report about accuracy, and where the kernels exist. Weights are only part of the budget: the KV cache, activations and optimiser state come on top, and the KV cache dominates at long context. For the background see /topics/efficient-ml/ and /topics/hardware-and-acceleration/.

Number formats

Memory figures are weights only, computed as parameters times bytes per parameter, rounded. “Bits” counts the scales and zero points that block formats carry alongside the weights.

Format Bits per weight Bytes per parameter 7 B weights 70 B weights Reported accuracy impact Hardware support
FP32 32 (1 sign, 8 exponent, 23 mantissa) 4.0 28 GB 280 GB The reference Everything
TF32 19 used (8 exponent, 10 mantissa), stored as FP32 4.0 28 GB 280 GB A compute format, not a storage one. NVIDIA reports matching FP32 training accuracy on its tested models NVIDIA tensor cores from Ampere (2020), on by default for convolutions in PyTorch, off for matmul unless you set torch.backends.cuda.matmul.allow_tf32 = True
BF16 16 (8 exponent, 7 mantissa) 2.0 14 GB 140 GB Kalamkar et al. (2019) report training matching FP32 across CNNs, RNNs and transformers without loss scaling NVIDIA Ampere and later, AMD MI200 and later, TPU (its native format), Apple M2 and later, x86 CPUs with AVX-512 BF16 or AMX
FP16 16 (5 exponent, 10 mantissa) 2.0 14 GB 140 GB Micikevicius et al. (2017) report matching FP32 with a master copy and loss scaling. Overflows above 65,504, so activations can blow up Nearly every GPU since 2017 (NVIDIA Volta tensor cores, Pascal with reduced throughput), Apple Silicon, ARM CPUs with FP16 extensions
FP8 E4M3 8 (4 exponent, 3 mantissa) 1.0 7 GB 70 GB Micikevicius et al. (2022) report training and inference results matching 16-bit on GPT-3 scale models with per-tensor scaling, E4M3 for weights and activations NVIDIA Hopper and Ada (2022) and later, AMD MI300, via Transformer Engine or torch.float8_e4m3fn
FP8 E5M2 8 (5 exponent, 2 mantissa) 1.0 7 GB 70 GB Same paper: the wider-range variant, used for gradients in the backward pass Same hardware
INT8 (weights and activations) 8 1.0 7 GB 70 GB Dettmers et al. (2022, LLM.int8) report no perplexity degradation up to 175 B once outlier features are kept in 16-bit. Xiao et al. (2022, SmoothQuant) report matching FP16 accuracy for W8A8 on OPT and BLOOM by migrating activation outliers into the weights NVIDIA Turing and later tensor cores, Intel CPUs with VNNI or AMX, most mobile NPUs, AMD MI200 and later
INT4 (weight only, plain) 4 plus a scale per group 0.5 to 0.56 3.5 to 3.9 GB 35 to 39 GB Depends entirely on the rounding method. Round-to-nearest at 4-bit without calibration is where GPTQ and AWQ start from and both report it degrades noticeably on small models No native INT4 tensor cores on Hopper (removed after Ampere). Weights are dequantised to 16-bit in the kernel. Blackwell adds FP4
NF4 (bitsandbytes) 4 plus a block scale (double-quantised) about 0.53 3.7 GB 37 GB Dettmers et al. (2023, QLoRA) report that 4-bit NF4 fine-tuning with LoRA matches 16-bit full fine-tuning on their benchmarks, and that NF4 beats plain FP4 and INT4 at the same size Software only: CUDA kernels in bitsandbytes, with ROCm and CPU backends newer and slower. Dequantised to BF16 for every matmul, so it is slower than 16-bit

Model-file formats

These are packaging schemes that carry their own quantisation, used for inference rather than training.

Format Bits per weight Bytes per parameter 7 B file 70 B file Reported accuracy impact Hardware support
GGUF Q8_0 8 plus one FP16 scale per 32 weights, 8.5 1.06 7.4 GB 74 GB The llama.cpp maintainers’ perplexity tables show it within noise of FP16 on Llama models CPU (AVX2, AVX-512, NEON), Metal, CUDA, Vulkan, ROCm, SYCL, through llama.cpp and Ollama
GGUF Q6_K about 6.6 0.82 5.7 GB 57 GB Maintainers’ tables: very small perplexity increase Same
GGUF Q5_K_M about 5.7 0.71 5.0 GB 50 GB Maintainers’ tables: small increase, usually the quality choice when it fits Same
GGUF Q4_K_M about 4.85 (4-bit weights, 6-bit block scales, some tensors at higher precision) 0.61 4.2 GB 42 GB Maintainers’ tables: a noticeable but small perplexity increase, the default most tools pick Same
GGUF Q4_0 4.5 0.56 3.9 GB 39 GB The legacy 4-bit scheme, worse than Q4_K_M at nearly the same size. ARM-optimised repacked variants exist Same, fastest on some ARM CPUs
GGUF Q3_K_M and Q2_K about 3.9 and 3.4 0.49 and 0.42 3.4 and 2.9 GB 34 and 29 GB Maintainers’ tables: clear degradation, worst on small models. Importance-matrix (imatrix) quantisation recovers part of it Same
GPTQ (4-bit, group 128) 4 plus FP16 scale and zero per 128, about 4.25 0.53 3.7 GB 37 GB Frantar et al. (2022) report negligible accuracy loss at 4-bit on OPT-175B and BLOOM-176B using second-order error correction, and usable 3-bit CUDA and ROCm kernels (Marlin, ExLlama) in vLLM, GPTQModel and Transformers. Weight-only, dequantised in the kernel
AWQ (4-bit, group 128) about 4.25 0.53 3.7 GB 37 GB Lin et al. (2023) report lower perplexity than GPTQ and round-to-nearest at the same bit width on Llama and OPT, by scaling the 1 % of channels that activations show to matter, with no backpropagation needed CUDA kernels in vLLM, AutoAWQ and TensorRT-LLM. Weight-only

The three 4-bit families land within 0.1 bytes per parameter of each other. Pick by where you run: GGUF for llama.cpp and Ollama, AWQ or GPTQ for vLLM on NVIDIA, NF4 for fine-tuning with QLoRA. Converting between them means re-quantising from the 16-bit original, not from each other.

What the numbers hide

  • Weights are not the whole budget. A 7 B model at 4-bit is 4 GB of weights, but a 32 k context with a 32-layer, 8 KV-head model adds about 1 GB of KV cache per sequence at FP16. Serving engines quantise the cache too (FP8 KV cache in vLLM and TensorRT-LLM, Q8 in llama.cpp).
  • Perplexity is the metric every paper reports and it understates damage on tasks with a single right answer, such as code and maths. Check a downstream benchmark on your task, with a confidence interval (see /reference/evaluation-metrics/).
  • Small models lose more. The papers’ headline numbers are for 65 B to 175 B models. A 1 B or 3 B model at 4-bit can lose several points on reasoning benchmarks.
  • Weight-only formats help memory and the memory-bound decode phase. They do not speed up prefill, which is compute-bound and runs in 16-bit after dequantisation. W8A8 and FP8 do speed up both.
  • Quantised fine-tuning (QLoRA) trains adapters in 16-bit on top of frozen 4-bit weights. The base model’s rounding error is baked in.
  • Speed depends on the kernel more than the format. The same AWQ checkpoint can run faster or slower than FP16 depending on batch size and GPU generation. Measure.

A worked int8 quantisation

Symmetric int8 maps the range [-max|w|, max|w|] onto [-127, 127] with one scale. Per-tensor scaling lets a single outlier waste most of the levels. Per-row (per-channel) scaling fixes that at the cost of one scale per output row, and is what every INT8 scheme in the table does.

import numpy as np

rng = np.random.default_rng(0)
w = rng.normal(0.0, 0.02, size=(256, 1024)).astype(np.float32)   # a weight matrix like a small linear layer
w[3, 17] = 0.5                                                   # one outlier, as real weights have

def quantise_int8_symmetric(x: np.ndarray, axis=None):
    """Symmetric int8: q = round(x / s), s = max|x| / 127. axis=None is per-tensor, axis=1 per output row."""
    amax = np.abs(x).max() if axis is None else np.abs(x).max(axis=axis, keepdims=True)
    scale = np.maximum(amax, 1e-12) / 127.0
    q = np.clip(np.rint(x / scale), -128, 127).astype(np.int8)
    return q, scale.astype(np.float32)

def dequantise(q, scale):
    return q.astype(np.float32) * scale

for name, axis in [("per-tensor", None), ("per-row", 1)]:
    q, s = quantise_int8_symmetric(w, axis)
    w_hat = dequantise(q, s)
    err = w_hat - w
    rel = np.linalg.norm(err) / np.linalg.norm(w)
    zeros = (q == 0).mean()
    print(f"{name:11} bytes/param 1.0  relative error {rel:.4f}  max abs error {np.abs(err).max():.5f}  weights rounded to zero {zeros:.1%}")

print("fp32 bytes:", w.nbytes, " int8 bytes:", w.size, " ratio:", w.nbytes / w.size)
per-tensor  bytes/param 1.0  relative error 0.0568  max abs error 0.00197  weights rounded to zero 7.8%
per-row     bytes/param 1.0  relative error 0.0087  max abs error 0.00197  weights rounded to zero 1.1%
fp32 bytes: 1048576  int8 bytes: 262144  ratio: 4.0

One outlier at 0.5 in a matrix of weights around 0.02 makes the per-tensor scale 25 times coarser than the rows need, rounds 7.8 % of the weights to zero and multiplies the error by six. Per-row scaling leaves one bad row and 255 good ones. Group-wise scaling (every 32 or 128 weights, as in GGUF, GPTQ and AWQ) is the same idea taken further, which is why those formats carry scales and why their bits per weight are not round numbers.

Sources