APN Mojo documentation

Arbitrary-precision numbers for Mojo

APN Mojo brings arbitrary-precision arithmetic and array programming together in one pure Mojo library. It includes exact integers and fractions, correctly rounded floating-point and complex arithmetic, interval arithmetic with guaranteed bounds, and multidimensional arrays of these number types. Write scalar functions once, then reuse them across arrays and build reductions, accumulations, and outer products with its vectorization tools.

All of this runs in Mojo, without a runtime dependency on GMP, MPFR, MPC, or Python. APN Mojo supports Linux and macOS with Mojo 1.1.0.

Integers

Exact whole numbers, with bit operations, integer roots, modular arithmetic, primality tests, and combinatorics.

Rationals

Fractions in lowest terms, including exact decimal input such as "0.1".

Floats

Choose a binary precision, exponent range, rounding mode, and error traps. Each arithmetic operation rounds once to the chosen format.

Complex numbers

Choose exact rational components with ExactComplex, or independently rounded Float components with Complex, including elementary functions.

Ball intervals

Carry real intervals or complex rectangles through calculations, and find out what their guaranteed bounds establish.

Batches and vmap

Store multidimensional arrays, select elements with masks, and map scalar functions across their values.

Reductions

sum and dot keep intermediates exact and round once. lift builds reductions, accumulations, and outer products from binary functions.

First program

After installing the package or a checkout, run this program from the directory containing pixi.toml. From a checkout:

pixi run --locked mojo run -I src docs/examples/quickstart.mojo

With the apn_mojo package installed, save it as quickstart.mojo and run pixi run mojo run quickstart.mojo, without -I src.

quickstart.mojo Download
"""Exact integers and fractions, Floats of any precision and Complex numbers."""

from apn_mojo import ArithmeticContext, Complex, Float, FloatFormat, Integer, Rational, sqrt


def main() raises:
    # Integers and Rationals are exact: they grow instead of overflowing or rounding.
    print("2 ** 100 =", Integer(2) ** 100)
    print("1/3 + 1/6 =", Rational(1, 3) + Rational(1, 6))
    # A Float has the precision you choose and rounds each result once.
    var precise = ArithmeticContext(format=FloatFormat(256))
    var root = sqrt(Float(2, context=precise))
    print("sqrt(2) =", root.to_string(10, digits=60))
    # Decimal text is exact; a native Float64 brings its binary rounding error along.
    print("0.1 from text =", Float("0.1", context=precise).to_string(10, digits=60))
    print("0.1 as Float64 =", Float(Float64(0.1), context=precise).to_string(10, digits=60))
    # Complex numbers round each component once.
    var z = Complex("1+2j") * Complex("3-4j")
    print("(1+2j)(3-4j) =", String(z.real().to_integer_exact()) + "+" + String(z.imag().to_integer_exact()) + "j")
    var w = sqrt(Complex(-4))
    print("sqrt(-4) =", String(w.real().to_integer_exact()) + "+" + String(w.imag().to_integer_exact()) + "j")

Run from the repository root pixi run mojo run -I src docs/examples/quickstart.mojo

Output

2 ** 100 = 1267650600228229401496703205376
1/3 + 1/6 = 1/2
sqrt(2) = 1.41421356237309504880168872420969807856967187537694807317668
0.1 from text = 0.100000000000000000000000000000000000000000000000000000000000
0.1 as Float64 = 0.100000000000000005551115123125782702118158340454101562500000
(1+2j)(3-4j) = 11+2j
sqrt(-4) = 0+2j

The integers and fractions in this example stay exact as their storage grows. The Float calculation uses 256 bits of precision, so sqrt(2) returns the correctly rounded 256-bit approximation. Each operation has this rounding guarantee; a sequence of operations can still accumulate error.

Supply decimal values as text to avoid rounding them through a native float first. Rational("0.1") is exactly one tenth; Float("0.1") rounds one tenth to a binary approximation. Importing Float64(0.1) starts with the approximation already stored in the native float.

Number families

Family What it holds Rounding
Integer Signed whole numbers Exact, within storage limits
Rational Fractions in lowest terms Exact, within storage limits
ExactComplex A real and an imaginary Rational Exact, within storage limits
Float Binary floating-point values with a chosen precision and exponent range Once per operation
Complex A real and an imaginary Float Once per component
Ball A real interval described by a midpoint and radius The bounds include input uncertainty and rounding error
ComplexBall A rectangle described by two Ball components Each component encloses all possible results

Integer and rational arithmetic stays exact: Integer + Rational returns a Rational. Mixing with a Float produces a rounded result. Named functions such as add and sqrt accept a context when the selected number family needs one. See the family references for accepted operands and return types.

Batches, mapping and reductions

Batch[T] holds Integer, Rational, Float, Complex, Ball, or ComplexBall values. Copies and slices keep their values when the original changes. Integer, rational, float, and complex batches provide arithmetic operators, comparisons, reductions, and JSON. Ball batches support NumPy-style functions such as batch.add and batch.exp, and mapping with vmap or lift. Real Ball batches support min and max; both ball families support cumsum and cumprod, but have no arithmetic operators or batch JSON. ExactComplex is a scalar type, with no batch support.

This example applies a scalar function to a batch, then reduces its results:

batch_quickstart.mojo Download
"""A first batch calculation with an explicit precision and a reduction."""

from apn_mojo import ArithmeticContext, Batch, FloatFormat, Integer, batch


def main() raises:
    var values = Batch[Integer]([1, 4, 9, 16])
    var context = ArithmeticContext(format=FloatFormat(256))
    var roots = batch.sqrt(values, context=context)
    print("roots:", roots)
    print("sum:", batch.sum(roots, context=context))

Run from the repository root pixi run mojo run -I src docs/examples/batch_quickstart.mojo

Output

roots: [1.0, 2.0, 3.0, 4.0]
sum: 10.0

With vmap[f], you can apply library functions or your own scalar functions across batch arguments. lift[f] builds on binary functions for integers, rationals, floats, complex numbers, and balls. Large supported operations can use a worker pool; short runs and some mapping signatures run on the caller thread. The batch reference describes those rules.

For floating-point and complex totals, sum and dot keep intermediates exact and round only the final result. The reduction reference also covers explicit rounding orders, axes, contexts, and custom folds.

Find your starting point

I want to... Read
Do exact integer arithmetic Integer values
Keep fractions exact Exact fractions
Compute with a chosen precision Floats of any precision
Work with exact or rounded complex numbers Complex numbers
Carry guaranteed interval bounds Calculations with bounds
Choose precision and check reliable digits Precision and accuracy
Work on many values at once Batches and selections
Map a custom function or build a fold Vectorization with vmap and lift
Choose a thread count Thread controls
Export native arrays Native values
Handle bad input or a failed update Errors and recovery
Save values without losing digits Text and JSON
Look up a function or type API reference
Browse runnable examples Examples
Understand the implementation Architecture

See support and limitations for supported platforms and library boundaries.