What it is
A number is an array of digits in its own base, a sign, the position of its point, and a precision counted in places. Arithmetic works on those digits directly.
Decimal, so 0.1 is 0.1
There is no binary representation error: a cent stays a cent. In base 2, a tenth never ends.
0.1 = 0.000110011001100110011001…
Correctly rounded, everywhere
Each function works past its answer with guard digits. When an answer lands near a rounding boundary, it is taken again, wider, until it is clear. A value exactly on a half is decided with whole numbers.
Any base from 2 to 16
Odd bases included, rounding at a half included. An answer comes back in its left operand's base.
0x15 + 0b1010 = 1F, in base 16
Angles as people write them
Radians, degrees, grads or millicles, set for each thread. Outside radians an angle is reduced by whole turns exactly, so sin 30° is exactly 0.5 and tan 90° is a pole, flagged infinite.
- functions
- 73
- more on complex numbers
- 44
- places by default
- 36
- places at most
- 100,000
- source files, no dependencies
- 2
Get started
Addaptic is two source files of standard C++17, LZMath.cpp and LZNumber.cpp, on GitHub. Add them to your build and include LZMath.h.
#include <cstdio>
#include <cstdlib>
#include "LZMath.h"
int main()
{
lzu_setup();
glWorkingPrecision = 50; // places after the point
CLZNumber two( "2" ), root;
lz_root_2( &two, &root ); // correctly rounded at 50 places
char* digits = root.GetStringRep();
printf( "%s\n", digits );
free( digits );
lzu_takedown();
}
This program prints the square root of 2, correctly rounded at 50 places.
$ c++ -std=c++17 -O2 root2.cpp LZMath.cpp LZNumber.cpp -o root2
$ ./root2
1.41421356237309504880168872420969807856967187537695
Results land in a target, or in the left operand when there is none. Errors come back as codes, and a result outside a function's domain is flagged as infinite or not-a-number.
Every function has a page in the reference, with examples the engine computed.
How we know it's right
Correct rounding is checked against independent arithmetic, on every change.
- 2,872
checks in the test suite, every one passing.
- 2,328 of 2,328
answers in the rounding audit correctly rounded at 36 places, for arguments from 10−40 to 1040, checked against Python's
decimal. - 20,000+
answers steered to within a millionth of a unit of a rounding boundary, at 36 to 1,000 places in bases 10, 16, 2 and 7. All round correctly.
- 0 warnings
under GCC, clang and MSVC. Every change is built and tested on Linux, under AddressSanitizer, UBSan and LeakSanitizer too, and on Windows.
Correct rounding is measured, not proven. Each function's doubt window is set at about ten times its largest measured error.
How it compares
Addaptic's place is the combination: decimal or any base, the circular, hyperbolic and gamma families, correct rounding, and no dependencies. MPFR has the functions and correct rounding but works in binary. Python's decimal module and Boost's decimal type work in base 10 but stop short of one or the other.
| Library | Radix | Functions | Rounding | Dependencies and licence |
|---|---|---|---|---|
| Addaptic | Any base 2–16 | 73, including circular, hyperbolic, gamma and log-gamma; 44 more on complex numbers | Correctly rounded, verified by measurement | None; MIT |
| MPFR | 2 | Full set, including sine and gamma | Correctly rounded | Requires GMP; LGPL 3 |
| Python decimal | 10 | exp, ln, log10, sqrt, power; no circular functions | exp, ln and log10 correctly rounded; power “almost always” in the C version | Part of Python; libmpdec in C |
| Boost cpp_dec_float | 10 | Through Boost.Math | “Always truncating”, with guard digits | Header-only; Boost licence |
Machine double | 2 | The C library's | Arithmetic correctly rounded; library functions generally not guaranteed | Built in, and far faster |
Know before you choose it
- It is slower than hardware. At 36 places an addition takes about 0.10 µs and a division 0.38 µs on an Apple Silicon Mac, roughly a hundred times what floating point takes.
- Precision counts places after the point, not significant digits. At 36 places, 10−30 keeps six significant digits.
- Infinities carry no sign, unlike IEEE 754. Infinity minus infinity is not-a-number.
- The API is free functions over a number class, with error codes rather than exceptions. Each thread keeps its own precision and angle unit.