ULP Accuracy Measurement Tool
Standalone tool for measuring the accuracy of Eigen's vectorized math functions in units of ULP (Unit in the Last Place). Compares Eigen's SIMD implementations against either MPFR (128-bit high-precision reference) or the standard C++ math library.
Building
From the Eigen build directory:
cd build
cmake ..
cmake --build . --target ulp_accuracy
If MPFR and GMP are installed, the build automatically enables MPFR support
(EIGEN_HAS_MPFR). Without them, only --ref=std is available.
Installing MPFR (Debian/Ubuntu)
sudo apt install libmpfr-dev libgmp-dev
Usage
./test/ulp_accuracy [options]
Options:
--func=NAME Function to test (required unless --list)
--lo=VAL Start of range (default: -inf)
--hi=VAL End of range (default: +inf)
--double Test double precision (default: float)
--step=EPS Sampling step: advance by (1+EPS)*nextafter(x)
(default: 0 = exhaustive; useful for double, e.g. 1e-6)
--threads=N Number of threads (default: all cores)
--batch=N Batch size for Eigen eval (default: 4096)
--ref=MODE Reference: 'std' (default) or 'mpfr'
--hist_width=N Histogram half-width in ULPs (default: 10)
--list List available functions
Examples
List all supported functions:
./test/ulp_accuracy --list
Exhaustive float test of sin against std (tests all ~4.28 billion finite floats):
./test/ulp_accuracy --func=sin
Float test against MPFR (more accurate reference, but slower):
./test/ulp_accuracy --func=sin --ref=mpfr
Double precision test with geometric sampling (exhaustive is impractical for double):
./test/ulp_accuracy --func=exp --double --step=1e-6
Test a specific range:
./test/ulp_accuracy --func=sin --lo=0 --hi=6.2832
Output
The tool prints:
- Test configuration: function, range, reference mode, thread count
- Max |ULP error|: worst-case absolute ULP error with the offending input value
- Mean |ULP error|: average absolute ULP error across all tested values
- Signed ULP histogram: distribution of signed errors showing bias direction
Example output:
Function: sin (float)
Range: [-inf, inf]
Representable values in range: 4278190082
Reference: MPFR (128-bit)
Threads: 32
Batch size: 4096
Results:
Values tested: 4278190081
Time: 529.04 seconds (8.1 Mvalues/s)
Max |ULP error|: 2
at x = -1.5413464e+38 (Eigen=-0.482218683, ref=-0.482218742)
Mean |ULP error|: 0.0874
Signed ULP error histogram [-10, +10]:
-2 : 51988 ( 0.001%)
-1 : 186805349 ( 4.366%)
0 : 3904475407 ( 91.265%)
1 : 186805349 ( 4.366%)
2 : 51988 ( 0.001%)
How it works
-
Range splitting: The input range is divided evenly across threads by splitting the linear ULP space.
-
Batched evaluation: Each thread fills batches of input values, evaluates them through Eigen's vectorized path (using
Eigen::Arrayoperations), and computes reference values one at a time. -
ULP computation: IEEE 754 bit patterns are mapped to a linear integer scale where adjacent representable values are adjacent integers. The signed ULP error is the difference between Eigen's result and the reference on this scale. Special cases (NaN, infinity mismatches) report infinite error.
-
Result reduction: Per-thread statistics (max error, mean error, histogram) are merged after all threads complete.
Supported functions
| Category | Functions |
|---|---|
| Trigonometric | sin, cos, tan, asin, acos, atan |
| Hyperbolic | sinh, cosh, tanh, asinh, acosh, atanh |
| Exponential/Log | exp, exp2, expm1, log, log1p, log10, log2 |
| Error/Gamma | erf, erfc, lgamma |
| Other | logistic, sqrt, cbrt, rsqrt |
File organization
ulp_accuracy.cpp— Main tool: ULP computation, worker threads, CLI, result printingmpfr_reference.h— MPFR reference function wrappers and scalar conversion helpers
Performance tips
- Float exhaustive sweeps test ~4.28 billion values. With
--ref=stdthis takes ~50 seconds per function; with--ref=mpfrit takes ~500 seconds (10x slower). - For double precision, exhaustive testing is impractical. Use
--step=1e-6to sample ~2.88 billion values geometrically. - Thread count defaults to all available cores. MPFR is the bottleneck (single MPFR call per value per thread), so more cores help significantly.