Skip to main content

custom_float_lib

A from-scratch IEEE-754-style floating point simulator for exploring low-precision number formats used in LLM quantization (FP8, FP6, FP4, bfloat16, TF32, and the OCP Microscaling family) - built to understand why these formats work the way they do, not just to call a library that already does it.

What this is

Every format in this library is implemented at the bit level: sign, exponent, mantissa, biasing, subnormals, infinities, and NaN - built up from IEEE-754 binary64 decoding rather than wrapping an existing quantization library. The goal was to actually understand quantization mechanics (guard/round/sticky rounding, round-to-nearest-even tie breaking, exponent overflow, subnormal underflow) well enough to explain every number it produces, then use that understanding to quantize a real language model (GPT-2) and measure the real accuracy/size tradeoff.

Features

  • 10 named format presets covering general-purpose training formats (bf16, fp16, tf32) and the OCP Microscaling family used on current accelerator hardware (MXFP8, MXFP6, MXFP4 / NVFP4), plus additional FP8 splits from quantization research
  • Three rounding modes: round-to-nearest-even (default), truncate, and stochastic rounding (with seeded reproducibility)
  • Vectorized batch quantization (quantize_array) using real numpy bit operations - not a Python loop - validated bit-for-bit identical to the scalar path across thousands of constructed test cases
  • Per-tensor scaling (quantize_array_scaled) to keep narrow formats (FP4/FP6) from overflowing to inf on real weight distributions
  • A working end-to-end LLM quantization pipeline: load a real HuggingFace model, quantize its weights through any preset, and measure the perplexity impact against the fp32 baseline

Format presets

Preset Bits (sign+exp+mantissa) Notes
bf16 1+8+7 Default mixed-precision training format
fp16 1+5+10 IEEE half precision
tf32 1+8+10 NVIDIA tensor-core training format
mxfp8_e4m3 1+4+3 OCP standard, precision-favoring FP8
mxfp8_e5m2 1+5+2 OCP standard, range-favoring FP8 (gradients)
mxfp6_e3m2 1+3+2 OCP MXFP6, range-favoring
mxfp6_e2m3 1+2+3 OCP MXFP6, precision-favoring
mxfp4_e2m1 1+2+1 OCP MXFP4 / NVIDIA NVFP4, native on Blackwell
fp8_e3m4 1+3+4 Mantissa-favoring FP8, weight-quantization research
fp8_e4m3fn 1+4+3 Finite-only OCP variant of e4m3

Quickstart

from custom_float_lib import CustomFloat
import numpy as np

# Scalar
cf = CustomFloat.mxfp8_e4m3()
cf.quantize(3.14159265)  # -> 3.25

# Vectorized (real model tensors, not just scalars)
weights = np.random.randn(4096, 4096)
quantized = cf.quantize_array(weights)

# Rounding modes
cf = CustomFloat.mxfp8_e4m3(rounding="stochastic", seed=42)  # unbiased in expectation
cf = CustomFloat.mxfp8_e4m3(rounding="truncate")              # cheap, deterministic, biased

# Per-tensor scaling - required for narrow formats (FP4/FP6) to avoid
# overflowing to inf on real weight magnitudes
quantized, scale = cf.quantize_array_scaled(weights)

Quantizing a real model

from transformers import GPT2LMHeadModel
from custom_float_lib import CustomFloat
from quantize_model import quantize_model_copy, evaluate_loss, perplexity_from_loss

model = GPT2LMHeadModel.from_pretrained("gpt2")
cf = CustomFloat.mxfp8_e4m3()
quantized_model = quantize_model_copy(model, cf, verbose=True)

Validated results (GPT-2, 124M params)

Perplexity on a fixed eval sample, fp32 baseline = 97.37:

Format Perplexity Δ vs baseline
bf16 98.38 +1.01
fp16 97.09 -0.29
tf32 97.09 -0.29
mxfp8_e4m3 98.43 +1.05
mxfp8_e5m2 99.83 +2.46
fp8_e3m4 96.58 -0.79
fp8_e4m3fn 98.43 +1.05
mxfp6_e3m2 110.97 +13.59
mxfp6_e2m3 5,092.20 +4,994.82
mxfp4_e2m1 15,515.99 +15,418.62

8-bit formats are near-lossless, matching published results. The narrow 4-bit/6-bit formats degrade sharply under per-tensor scaling - this is a known, expected limitation (not a bug): real MXFP4/NVFP4 deployments always use per-block scaling plus additional calibration, which is the natural next experiment on top of this codebase.

On-disk size was independently verified, not just estimated: casting fp8_e4m3fn-quantized weights into PyTorch's native torch.float8_e4m3fn dtype produced a real 124.86 MB file against a 497.76 MB fp32 baseline (74.9% reduction), matching the theoretical bits-per-value estimate (124.80 MB) to within 0.06 MB.

Validation methodology

Every arithmetic path was cross-checked before being trusted on a real model:

  • Scalar vs. vectorized outputs compared bit-for-bit across special values, random normals, deliberately constructed subnormal-triggering and overflow-triggering values, and constructed RNE tie-boundary cases, across all 10 presets
  • Stochastic rounding validated for seeded reproducibility (both scalar and vectorized paths) and empirically confirmed unbiased: mean of 20,000 draws landed ~73x closer to the true value than RNE's single deterministic estimate, on a coarse format
  • Root-caused every anomaly found (FP64 exponent field width bug, reserved-exponent collision on overflow, subnormal boundary handling) back to the bit-level cause rather than patching symptoms

Known limitations / roadmap

See TODO.md for the full list. Notably:

  • Overflow currently saturates to infinity; a saturate-to-max-finite mode (matching real hardware formats like e4m3fn) is not yet implemented
  • Scaling is per-tensor only; per-block scaling (what real MX hardware uses) is not yet implemented - this is the most impactful next step for narrow-format accuracy
  • No native low-bit storage for formats without a PyTorch dtype (mxfp6, mxfp4) - quantization here simulates precision loss but does not shrink files on disk for those formats
  • Not yet packaged for PyPI (no pyproject.toml, no formal test suite)

Project structure

custom_float_lib/
├── __init__.py       # public API re-exports
├── core.py            # CustomFloat class, scalar quantize/encode/decode
├── presets.py          # named format presets
└── vectorized.py       # numpy-vectorized batch quantization
quantize_model.py        # torch model quantization + eval helpers

Metadata

Release files for custom-float-quant 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for custom-float-quant 0.1.0
File Size Uploaded
custom_float_quant-0.1.0.tar.gz 18.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for custom-float-quant 0.1.0
File Interpreter ABI Platform
custom_float_quant-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 34.1 kB

Release files / custom_float_quant-0.1.0.tar.gz

Download URL custom_float_quant-0.1.0.tar.gz
Size 18.8 kB
Tags Source
SHA-256 checksum
How to use checksums
5b7bddd69167a19b3ff47cbc82f5b1afdb46921345015373b460acd030471b70
BLAKE2b-256 checksum
How to use checksums
5337b9bce317ab0688d72758ec63b7c8826d674adb2480ee138f3134d6181ecc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.10

Release files / custom_float_quant-0.1.0-py3-none-any.whl

Download URL custom_float_quant-0.1.0-py3-none-any.whl
Size 15.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1f3149f23aab2f7d6f22abb3d6a6351d7c60f599aa63d3a4cd84c508889fb866
BLAKE2b-256 checksum
How to use checksums
19e67e7e5fd73ffe3b08760881941342c36ca3dddc4fd7a77d5cb604592be476
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.10

Release history Release notifications | RSS feed

0.3.0

2 release files

0.2.1

2 release files

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page