An arbitrary-precision integer and decimal library for
Mojo, also with a 128-bit fixed-point decimal
type, inspired by Python’s int and Decimal. Install it with
pixi add decimo.
Comes with an interactive arbitrary-precision calculator (REPL + one-shot mode)
powered by ArgMojo. Install it with
brew install forfudan/tap/decimo.
The same library is packaged for Python as a near drop-in for the standard
library’s decimal, and a superset of it: the whole of its method surface,
plus pi(), e() and a 128-bit decimal type. Install it with
pip install decimo.
| Type | Alias | Information | Layout |
|---|---|---|---|
BigInt |
BInt |
Equivalent to Python’s int |
Base-2^64 |
BigDecimal |
BDec, Decimal |
Equivalent to Python’s decimal.Decimal |
Base-10^18 |
Decimal128 |
Dec128 |
128-bit fixed-precision decimal type | 32-bit words |
BigFloat |
Float |
Arbitrary-precision floating-point type | MPFR/GMP |

Base-ten arithmetic, integers with no width limit, and an expression evaluator
Decimo provides an arbitrary-precision integer and decimal library for Mojo. It delivers exact calculations for financial modeling, scientific computing, and applications where floating-point approximation errors are unacceptable. Beyond basic arithmetic, the library includes advanced mathematical functions with guaranteed precision.
For Pythonistas, decimo.BigInt to Mojo is like int to Python, and
decimo.BigDecimal to Mojo is like decimal.Decimal to Python.
decimo.Decimal128 to Mojo is like System.Decimal to C# or rust_decimal to
Rust.
The core types are[^auxiliary]:
BigInt[^bigint] (alias BInt),
which is a Mojo-native equivalent of Python’s int.BigDecimal) (alias Decimal)
allowing for calculations with unlimited digits and decimal
places[^arbitrary], which is a Mojo-native equivalent of Python’s
decimal.Decimal.Decimal128) (alias Dec128)
supporting up to 29 significant digits with a maximum of 28 decimal
places1, which is a Mojo-native equivalent of C#’s System.Decimal or
Rust’s rust_decimal.BigFloat) backed by
the GNU MPFR library, supporting computations with configurable precision and
a wide exponent range. Unlike BigDecimal, which uses base-10 arithmetic,
BigFloat uses binary floating-point internally. This type is optional and
requires MPFR/GMP to be installed on the user’s system.
Decimo is fast: at a million digits pi() is nearly twelve times quicker than
pure-Python mpmath, BigInt multiplication is fifteen times quicker than
CPython’s int,
and small BigDecimal operations are close to libmpdec, the C library behind
Python’s decimal. The measured numbers, with the commit they were taken on,
are in docs/benchmarks.md; pixi run benchdoc regenerates
them.
Decimo combines “Decimal” and “Mojo” - reflecting its purpose and implementation language. “Decimo” is also a Latin word meaning “tenth” and is the root of the word “decimal”.
decimo is a command-line calculator built on the Decimo library and powered by
ArgMojo. Run it with no arguments for an
interactive REPL, or pass an expression / file / piped stdin for one-shot
evaluation. The binary is self-contained — no Mojo or Pixi needed on the user’s
machine. See the user manual for the full
reference, and the Quick start below for a taste.

Ask for as many significant digits as you like

Everything decimal has, and things it does not
The Mojo library is also compiled into a Python extension and published on PyPI,
where it stands in for the standard library’s decimal. Change one import and
a decimal program keeps working:
# from decimal import Decimal, getcontext
from decimo import Decimal, getcontext
getcontext().prec = 50
print(Decimal(1) / Decimal(7))
# 0.14285714285714285714285714285714285714285714285714
The two agree digit for digit. The test suite checks every operation against
the standard library rather than against a table of expected strings, so
“agrees with decimal” is measured rather than claimed.
Nothing is missing: every method decimal.Decimal has, decimo.Decimal has
too. And it goes further. pi() and e() are there, which decimal has
neither of. sqrt, exp, ln and log10 take a rounding= argument, where
decimal ignores the context mode for those and always rounds half to even.
Decimal128 brings trigonometry, cbrt, root and the IEEE 754 interchange
bytes with it. And it is faster once the numbers are large — 2.9x at a
thousand digits — 20-25% behind on small ones, where what is left is the cost
of the Python call rather than the arithmetic.
Where it is not a drop-in it refuses rather than answers differently: no NaN
or infinity, no ROUND_05UP, no signals or traps, and one context per process
rather than per thread. See python/README.md for the
full list with the reasoning, and the
Python quick start below for a taste.
This repository includes a built-in TOML parser
(decimo.toml), a lightweight pure-Mojo implementation supporting TOML v1.0. It
parses configuration files and test data, supporting basic types, arrays, and
nested tables. While created for Decimo’s testing framework, it offers
general-purpose structured data parsing with a clean, simple API.
Decimo is available in the modular-community
https://repo.prefix.dev/modular-community package repository. To access this
repository, add it to your channels list in your pixi.toml file:
channels = ["https://conda.modular.com/max", "https://repo.prefix.dev/modular-community", "conda-forge"]
Then, you can install Decimo using any of these methods:
From the pixi CLI, run the command pixi add decimo. This fetches the
latest version and makes it immediately available for import.
In the mojoproject.toml file of your project, add the following dependency:
decimo = ">=0.14.0, <0.15.0"
Then run pixi install to download and install the package.
For the latest development version in the main branch, clone
this GitHub repository and build the
package locally using the command pixi run package.
The decimo CLI is distributed via the
forfudan/tap Homebrew tap.
Pre-built binaries are available for macOS arm64 (Apple Silicon) and
Linux x86_64, and ship with the Mojo runtime libraries bundled — you do not
need Mojo or Pixi installed.
brew install forfudan/tap/decimo
decimo --version
Or tap once and use the bare formula name:
brew tap forfudan/tap
brew install decimo
To upgrade to a later release:
brew update && brew upgrade decimo
pip install decimo
Wheels are built for macOS arm64 (macOS 11 and later) and for Linux on x86_64 and arm64 (glibc 2.35 and later), for CPython 3.13 and 3.14. Nothing else is needed — the Mojo runtime libraries travel inside the wheel. On any other platform, build from source with pixi:
git clone https://github.com/forfudan/decimo && cd decimo
pixi run -e py314 release # or py313; the wheel lands in python/dist/
pip install python/dist/*.whl
You can start using Decimo by importing the decimo module. An easy way to do
this is to import everything from the prelude module, which provides the most
commonly used types.
from decimo.prelude import *
This will import the following types or aliases into your namespace:
BigInt (and its alias BInt): An arbitrary-precision signed
integer type, equivalent to Python’s int.BigDecimal (and its aliases BDec, Decimal): An arbitrary-precision
decimal type, equivalent to Python’s decimal.Decimal.Decimal128 (and its alias Dec128): A 128-bit fixed-precision decimal type.RoundingMode: An enumeration for rounding modes.ROUND_DOWN, ROUND_HALF_UP, ROUND_HALF_EVEN, ROUND_UP,
ROUND_CEILING, ROUND_FLOOR: Constants for common rounding modes.For an interactive session, just type decimo:

A real session: ans, the : settings system, and inline settings
$ decimo
Decimo — an arbitrary-precision calculator 🔥
Type ? for help, : for settings, :q to quit.
Precision: 50. Rounding: ROUND_HALF_EVEN.
decimo> 2 ^ 10
1024
decimo> ans / 4
256
decimo> 1/7
0.14285714285714285714285714285714285714285714285714
decimo> :100
Current settings:
Precision : 100
Scientific : off
Engineering : off
Pad : off
Delimiter : (none)
Rounding mode : ROUND_HALF_EVEN
decimo> pi
3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068
decimo> sqrt(e) / ln(10) + sin(-1.23) :200 e he delimiter _
-226.458_251_870_114_348_807_514_569_584_297_293_353_150_959_525_480_515_507_901_779_719_167_225_208_528_825_475_488_261_072_148_336_432_171_617_635_953_314_758_797_226_777_458_915_435_649_950_836_584_843_137_886_028_274_720_793_979_517_570_004_978_334_405_953_342_64E-3
decimo> :q
The REPL keeps the last result in ans, lets you define variables
(name = expr), and exposes settings via :-prefixed commands (e.g. :100 for
precision, :s for scientific, :d for ROUND_DOWN). Input is case-insensitive.
Quit with :q, exit, or Ctrl-D.
As an innovative feature, Decimo supports multiple settings in a single line.
They can either be global (persist across calculations) or local (apply only to
the current expression). In the example above, :200 e he delimiter _ means
“evaluate the expression with precision 200 (200), scientific notation with
engineering exponent (e), round half to even (he), and use _ as the digit
delimiter in the output (delimiter _)”. The settings apply only to the current
expression and do not affect subsequent calculations.
For one-shot evaluation, pass an expression on the command line, pipe it via stdin, or read from a file:
$ decimo "sqrt(2)" -P 30
1.41421356237309504880168872421
$ echo "1/3" | decimo -P 50
0.33333333333333333333333333333333333333333333333333
$ decimo -F expressions.dm -P 80
Useful flags: -P N (precision), -R MODE (rounding), -S / -E (scientific
/ engineering), --pad, --delimiter, --completions {bash,zsh,fish}. Run
decimo --help for the full list.
Everything a decimal program normally touches is there, under the same names:
from decimo import Decimal, Decimal128, getcontext, localcontext, ROUND_FLOOR
getcontext().prec = 28
# The operators, the context, and the methods, as in `decimal`.
Decimal("0.1") + Decimal("0.2") # 0.3, exactly
Decimal(1) / Decimal(7) # to the context precision
Decimal("2.675").quantize(Decimal("0.01"))
divmod(Decimal(17), Decimal(5)) # (3, 2)
# A context you can compute in, without touching the global one.
with localcontext(prec=50):
print(Decimal(2).sqrt())
# Three things `decimal` does not have.
import decimo
decimo.pi(1000) # Chudnovsky with binary splitting
decimo.e(50)
Decimal(2).sqrt(rounding=ROUND_FLOOR) # and exp, ln, log10, correctly rounded
# The fixed-width type for money: 16 bytes that own nothing.
price = Decimal128("19.99")
(price * 3).quantize(Decimal128("0.01")) # 59.97
A mixed expression settles in the wider type — Decimal128 + Decimal is a
Decimal, either way round — and the hashes of Decimal, Decimal128,
int, float and decimal.Decimal all agree, so the five are
interchangeable as dictionary keys.
What decimo refuses rather than answering differently: NaN and infinity,
ROUND_05UP, signals and traps, and one context per process rather than per
thread. The full list, with the reasoning, is in
python/README.md.
Financial calculations and data analysis require precise decimal arithmetic that floating-point numbers cannot reliably provide. As someone working in finance and credit risk model validation, I needed a dependable correctly-rounded, fixed-precision numeric type when migrating my personal projects from Python to Mojo.
Since Mojo currently lacks a native Decimal type in its standard library, I decided to create my own implementation to fill that gap.
This project draws inspiration from several established decimal implementations
and documentation, e.g.,
Python built-in Decimal type,
Rust rust_decimal crate,
Microsoft’s Decimal implementation,
General Decimal Arithmetic Specification,
etc. Many thanks to these predecessors for their contributions and their
commitment to open knowledge sharing.
Rome wasn’t built in a day. Decimo is currently under active development. It has successfully progressed through the “make it work” phase and the “make it right”, and is now well into the “make it fast” phase.
The BigInt type is fully implemented and optimized. It is measured against
GMP, timed in C, rather than against CPython’s int, which is reached through
the interpreter and so loses on call overhead before the arithmetic starts.
Bug reports and feature requests are welcome! If you encounter issues, please file them here.
After cloning the repo onto your local disk, you can:
pixi run test to run all tests, or pixi run test <suite> for one suite
(pixi run test --list shows them).pixi run testcli to run CLI calculator tests.pixi run testpy to build the Python extension and run its tests against
the standard library’s decimal.pixi run bench to run benchmarks.pixi run benchdoc to regenerate docs/benchmarks.md
against libmpdec, GMP, CPython and MPFR. Needs mpdecimal and gmp
installed for the C comparisons; the reference libraries for the pi() table
live in the optional
benchdoc environment (pixi install -e benchdoc).pixi run buildcli to compile the CLI calculator to a ./decimo binary.pixi run organize_imports to group, sort and de-duplicate the imports
in every .mojo file: four blocks separated by a blank line — the Mojo
standard library, third-party packages, decimo itself, and modules reached
through -I. --check is what the pre-commit hook runs. A file whose import
block holds a comment is left alone and named as skipped;
scripts/organize_mojo_imports.py’s docstring says why, and why
--remove-unused is opt-in.pixi run check_import_fixed_point after changing the organizer or the
Mojo version: it asserts that the organizer and mojo format do not undo
each other’s work.If you find Decimo useful, consider listing it in your citations.
@software{Zhu.2026,
author = {Zhu, Yuhao},
year = {2026},
title = {Decimo: An arbitrary-precision integer and decimal library for Mojo},
url = {https://github.com/forfudan/decimo},
version = {0.14.0},
note = {Computer Software}
}
This repository and its contributions are licensed under the Apache License v2.0.
The BigFloat type optionally uses the
GNU MPFR Library (LGPLv3+) and
GMP (LGPLv3+ or GPLv2+) at runtime. Decimo does not
include or distribute any MPFR/GMP source code or binaries — they are loaded via
dlopen only if the user has independently installed them. All other Decimo
types work without any external dependencies. See the NOTICE file
for details.
The Dec128 type can represent values with up to 29 significant
digits and a maximum of 28 digits after the decimal point. When a
value exceeds the maximum representable value (2^96 - 1), Decimo
either raises an error or rounds the value to fit within these
constraints. For example, the significant digits of
8.8888888888888888888888888888 (29 eights total with 28 after the
decimal point) exceeds the maximum representable value (2^96 - 1)
and is automatically rounded to 8.888888888888888888888888889 (28
eights total with 27 after the decimal point). Decimo’s Dec128 type
is similar to System.Decimal (C#/.NET), rust_decimal in Rust,
DECIMAL/NUMERIC in SQL Server, etc.
[^bigint]: The BigInt implementation uses a base-2^64 representation with a
little-endian format, where the least significant word is stored at
index 0. Each word is a UInt64, allowing for efficient storage and
arithmetic operations on large integers. This design choice optimizes
performance for binary computations while still supporting arbitrary
precision.
[^auxiliary]: The auxiliary types include a base-10 arbitrary-precision signed
integer type (BigInt10) and a base-10 arbitrary-precision
unsigned integer type (BigUInt) supporting unlimited
digits[^bigint10]. BigUInt is used as the internal
representation for BigInt10 and Decimal.
[^bigint10]: The BigInt10 implementation uses a base-10 representation for users
(maintaining decimal semantics), while internally using an
optimized base-10^18 storage system for efficient calculations. This
approach balances human-readable decimal operations with
high-performance computing. It provides both floor division (round
toward negative infinity) and truncate division (round toward zero)
semantics, enabling precise handling of division operations with
correct mathematical behavior regardless of operand signs.
[^arbitrary]: Built on the BigUInt implementation, Decimal
supports arbitrary precision for both the integer and fractional
parts, similar to decimal and mpmath in Python,
java.math.BigDecimal in Java, etc. ↩