mirror of
https://gitlab.com/libeigen/eigen.git
synced 2026-04-10 11:34:33 +08:00
146 lines
4.4 KiB
Markdown
146 lines
4.4 KiB
Markdown
# 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:
|
|
|
|
```bash
|
|
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)
|
|
|
|
```bash
|
|
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:
|
|
```bash
|
|
./test/ulp_accuracy --list
|
|
```
|
|
|
|
Exhaustive float test of sin against std (tests all ~4.28 billion finite floats):
|
|
```bash
|
|
./test/ulp_accuracy --func=sin
|
|
```
|
|
|
|
Float test against MPFR (more accurate reference, but slower):
|
|
```bash
|
|
./test/ulp_accuracy --func=sin --ref=mpfr
|
|
```
|
|
|
|
Double precision test with geometric sampling (exhaustive is impractical for double):
|
|
```bash
|
|
./test/ulp_accuracy --func=exp --double --step=1e-6
|
|
```
|
|
|
|
Test a specific range:
|
|
```bash
|
|
./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
|
|
|
|
1. **Range splitting**: The input range is divided evenly across threads by
|
|
splitting the linear ULP space.
|
|
|
|
2. **Batched evaluation**: Each thread fills batches of input values, evaluates
|
|
them through Eigen's vectorized path (using `Eigen::Array` operations), and
|
|
computes reference values one at a time.
|
|
|
|
3. **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.
|
|
|
|
4. **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 printing
|
|
- `mpfr_reference.h` — MPFR reference function wrappers and scalar conversion helpers
|
|
|
|
## Performance tips
|
|
|
|
- Float exhaustive sweeps test ~4.28 billion values. With `--ref=std` this takes
|
|
~50 seconds per function; with `--ref=mpfr` it takes ~500 seconds (10x slower).
|
|
- For double precision, exhaustive testing is impractical. Use `--step=1e-6` to
|
|
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.
|