addaptic

Every digit it is given.

Addaptic is arbitrary-precision decimal arithmetic for C++. It works on the digits themselves, in any base from 2 to 16, and rounds every answer correctly: arithmetic, roots, logarithms, trigonometry, the gamma function, and both parts of every complex result.

Same question, two answers
QuestionC++ doubleAddaptic
0.1 + 0.2 0.30000000000000004 0.3
sin 30° 0.49999999999999994double has no degrees, so sin(π/6) 0.5
(1020 + 1) − 1020 0 1
1 ÷ 3 in base 12 nonedouble has only base 2 0.4

Wrong digits are underlined. The double is printed to 17 significant digits, enough to show the value it holds. Addaptic writes an exact answer short, so its 0.3 is exactly three tenths.

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.

LibraryRadixFunctionsRoundingDependencies and licence
AddapticAny base 2–1673, including circular, hyperbolic, gamma and log-gamma; 44 more on complex numbersCorrectly rounded, verified by measurementNone; MIT
MPFR2Full set, including sine and gammaCorrectly roundedRequires GMP; LGPL 3
Python decimal10exp, ln, log10, sqrt, power; no circular functionsexp, ln and log10 correctly rounded; power “almost always” in the C versionPart of Python; libmpdec in C
Boost cpp_dec_float10Through Boost.Math“Always truncating”, with guard digitsHeader-only; Boost licence
Machine double2The C library'sArithmetic correctly rounded; library functions generally not guaranteedBuilt 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.