Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

decimo

A drop-in replacement for Python's decimal, written in Mojo.

PyPI License

⚠️ Development release. The API is settled enough to use, but the versions carry a timestamp: 0.15.0.devYYYYMMDDHHMMSS, where 0.15.0 is the version of the Mojo library the wheel packages. Wheels: macOS arm64 (11 and later) and Linux x86_64 and arm64 (glibc 2.35 and later), CPython 3.13 and 3.14. Everywhere else, build from source.

Change one import and your 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 libraries 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 a property that is measured, not a claim.

How fast is it?

The same benchmark file run against both libraries -- python/benchmarks/compare.py in the repository, which imports one or the other and runs identical code. Best of five, on an Apple M-series laptop:

Program decimo decimal
add/sub/mul/div, 28 digits 90.6 ns 73.0 ns 1.24× slower
add/sub/mul/div, 200 digits 331.8 ns 409.0 ns 1.23× faster
add/sub/mul/div, 1000 digits 2.02 µs 5.89 µs 2.91× faster
compound interest, 150 years 12.71 µs 13.08 µs about the same
e from its series, 500 digits 197.92 µs 162.75 µs 1.22× slower
sqrt by Newton, 1000 digits 143.29 µs 232.29 µs 1.62× faster
pi by Machin, 500 digits 620.12 µs 520.17 µs 1.19× slower
parse and print, 1000 digits 3.28 µs 2.60 µs 1.26× slower

decimo is faster once the numbers are large, and 20-25% behind on small ones. The small-number gap is not the arithmetic: measured against libmpdec directly, without an interpreter in the way, decimo is 2-4× faster at 9 digits on add, subtract, multiply and round, and faster at every operation at 1000 digits. What is left is the cost of the Python call itself -- building the result object and converting the operands -- which CPython's decimal has had thirty years to shave. See the benchmarks.

What works

Everything a decimal program normally touches:

  • all the operators, including //, %, divmod() and **
  • getcontext().prec and getcontext().rounding, setcontext(), localcontext() (with keyword overrides), Context objects
  • all the rounding modes: ROUND_HALF_EVEN, ROUND_HALF_UP, ROUND_HALF_DOWN, ROUND_DOWN, ROUND_UP, ROUND_CEILING, ROUND_FLOOR, exact for arithmetic, quantize, round() and to_integral_value()
  • int(), float(), round(), math.floor/ceil/trunc, hash(), format()
  • Context you can compute with: ctx.divide(x, y), ctx.sqrt(x), ctx.quantize(x, y) and the rest, none of them touching the current context
  • keyword arguments where decimal takes them: quantize(exp, rounding=ROUND_HALF_UP), to_integral_value(rounding=...), sqrt(context=...)
  • Decimal((sign, digits, exponent)), so as_tuple() round-trips
  • pow(x, y, modulus), by modular exponentiation
  • quantize, normalize, as_tuple, as_integer_ratio, compare, fma, sqrt, exp, ln, log10, scaleb, adjusted, copy_abs, copy_negate, copy_sign, same_quantum, to_eng_string, max, min
  • the rest of the specification's surface: remainder_near, next_plus, next_minus, next_toward, shift, rotate, logical_and, logical_or, logical_xor, logical_invert, logb, compare_total, compare_total_mag, compare_signal, max_mag, min_mag, number_class, to_integral_exact, is_normal, is_subnormal, from_number
  • the same coercion rules: int converts in arithmetic, float does not, and both convert in a comparison
  • copy, deepcopy and pickle
  • ZeroDivisionError where you expect it, and hashes that agree with int, float and decimal.Decimal

Four things decimal does not have

decimo.pi(1000)  # 1000 digits of pi, by Chudnovsky with binary splitting
decimo.e(50)  # 50 digits of e
Decimal(2).sqrt(rounding=ROUND_FLOOR)  # and exp, ln, log10 too
Decimal(1).sinh()  # and fifteen more, listed below

pi() and e() use the context precision when given no argument; decimal has neither, and its documentation gives a recipe to write your own.

The mathematics is sin, cos, tan, arctan, arcsin, arccos, arctan2, expm1, log1p, hypot, sinh, cosh, tanh, arcsinh, arccosh and arctanh. Each takes the context precision and an optional rounding=, like the four above, and each is correctly rounded. arctan2 and hypot take the second value first: y.arctan2(x) is atan2(y, x), and x.hypot(y) is the hypotenuse. expm1 and log1p are there for the same reason math has them -- near zero they keep the digits that exp(x) - 1 and ln(1 + x) lose.

rounding= on sqrt, exp, ln and log10 is decimo's own: decimal ignores the context mode for these and always rounds half to even. And the answer under any mode is decided rather than approximated. The library computes wider than you asked, takes the interval its own error bound allows, and checks that the whole of it rounds to one answer; if a boundary falls inside, it widens and looks again. The same check now guards the default half to even, where before the last digit was assumed rather than known -- so ROUND_FLOOR really does stay under the true value, and a tie really is a tie.

A second type for money

Decimal is arbitrary precision and is what a program reaching for decimal.Decimal wants. Decimal128 is the other one: 96 bits of coefficient and a scale from 0 to 28, in sixteen bytes that own nothing -- the layout .NET's System.Decimal and Rust's rust_decimal use.

from decimo import Decimal128  # Dec128 is the same type

price = Decimal128("19.99")
line = (price * 3).quantize(Decimal128("0.01"))  # 59.97
Decimal128(2).sqrt()  # and exp, ln, log10, sin, cos, tan
Decimal128("1").to_ieee754()  # the IEEE 754 interchange bytes

It does arithmetic, compares, hashes and rounds like Decimal, mixes with int, float and str on either side of an operator, and converts both ways (Decimal128(x).to_decimal(), Decimal(x)). Its results never allocate, so it is quicker than Decimal on every operation where the values are money and the shape of them is known, and quicker than decimal on all of them but str().

A mixed expression settles in the wider type: Decimal128(x) + Decimal(y) is a Decimal, either way round, because widening loses nothing.

What it does not do: it stops at 7.9E+28 and 28 decimal places and raises rather than rounding into a context, it has no Context of its own beyond the rounding mode, and its scale is never negative -- Decimal128("1.23E+5") is 123000, where decimal keeps a coefficient of 123 with an exponent of 3 and can print it as 123E+3. Decimal128(0.1) is 0.1 rather than the whole binary expansion, since 55 digits do not fit 28; what is promised is that float(Decimal128(x)) == x.

What does not

decimo refuses these rather than answering differently:

  • NaN and infinity. decimo has no non-finite values. is_nan() and is_infinite() are always False.
  • ROUND_05UP. Nothing implements it; setting it raises NotImplementedError.
  • sqrt, exp, ln and log10 ignore the context rounding, as they do in decimal, and round half to even unless you pass rounding=. ** follows the context, also as in decimal. All of them are correctly rounded whichever mode applies, as are arithmetic, quantize and round().
  • //, %, divmod() and remainder_near with a long quotient. decimal raises InvalidOperation when the integer quotient has more digits than the precision; decimo answers. The remainder is rounded to the context, as in decimal.
  • Emin only reaches four methods. Exponents are unbounded, so Emin changes nothing except what next_plus(0), next_minus(0), is_subnormal() and number_class() say, where decimal's answers need a smallest exponent.
  • Signals and traps. Context.flags and Context.traps exist and stay empty. Inexact, Rounded and the rest are importable but never raised.
  • Emin / Emax. Exponents are unbounded, so nothing ever underflows to zero or overflows to infinity. A loop that waits for a term to become exactly zero will not stop; wait for the sum to stop changing instead.
  • One context per process, not per thread.

DivisionByZero and InvalidOperation are aliases for ZeroDivisionError and ValueError, which is what decimo actually raises, so except clauses written against decimal still catch.

Installing

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. On anything else, 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

Metadata

Release files for decimo 0.16.0.dev20261009221123

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for decimo 0.16.0.dev20261009221123
File
decimo-0.16.0.dev20261009221123-cp314-cp314-manylinux_2_35_x86_64.whl CPython 3.14 CPython 3.14 Linux glibc 2.35+ x86-64 Details
decimo-0.16.0.dev20261009221123-cp314-cp314-manylinux_2_35_aarch64.whl CPython 3.14 CPython 3.14 Linux glibc 2.35+ ARM64 Details
decimo-0.16.0.dev20261009221123-cp314-cp314-macosx_11_0_arm64.whl CPython 3.14 CPython 3.14 macOS 11.0+ ARM64 Details
decimo-0.16.0.dev20261009221123-cp313-cp313-manylinux_2_35_x86_64.whl CPython 3.13 CPython 3.13 Linux glibc 2.35+ x86-64 Details
decimo-0.16.0.dev20261009221123-cp313-cp313-manylinux_2_35_aarch64.whl CPython 3.13 CPython 3.13 Linux glibc 2.35+ ARM64 Details
decimo-0.16.0.dev20261009221123-cp313-cp313-macosx_11_0_arm64.whl CPython 3.13 CPython 3.13 macOS 11.0+ ARM64 Details

Total release size: 8.5 MB

Release files / decimo-0.16.0.dev20261009221123-cp314-cp314-manylinux_2_35_x86_64.whl

Download URL decimo-0.16.0.dev20261009221123-cp314-cp314-manylinux_2_35_x86_64.whl
Size 1.6 MB
Tags CPython 3.14 Linux glibc 2.35+ x86-64
SHA-256 checksum
How to use checksums
699e39bacd5a4a881625262a840ecf8ee199ea8823672fb03bf5e1765a4bde5c
BLAKE2b-256 checksum
How to use checksums
c3c0d3a17d64e6372a785e4f92bc283241d16f1ffcf012ecc5e59c9937426cd5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 9, 2026.

Transparency log

Release files / decimo-0.16.0.dev20261009221123-cp314-cp314-manylinux_2_35_aarch64.whl

Download URL decimo-0.16.0.dev20261009221123-cp314-cp314-manylinux_2_35_aarch64.whl
Size 1.4 MB
Tags CPython 3.14 Linux glibc 2.35+ ARM64
SHA-256 checksum
How to use checksums
76fcf37baa1b88f93210ad12874c10040789fc07b1106c04bddd8aafdae9fb60
BLAKE2b-256 checksum
How to use checksums
0eb9e2fc8eecbd508548a538154d33fd91c71dab21bc68990ff040e6a2e2b361
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 9, 2026.

Transparency log

Release files / decimo-0.16.0.dev20261009221123-cp314-cp314-macosx_11_0_arm64.whl

Download URL decimo-0.16.0.dev20261009221123-cp314-cp314-macosx_11_0_arm64.whl
Size 1.3 MB
Tags CPython 3.14 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
f303f6cf1d499d9d2a6d203fd165ca6810a326d96d3d381225af466974f4e659
BLAKE2b-256 checksum
How to use checksums
82afcdad4c01d099daed13a045a87b1ce6591e9081310c9c241a59524b38ce0b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 9, 2026.

Transparency log

Release files / decimo-0.16.0.dev20261009221123-cp313-cp313-manylinux_2_35_x86_64.whl

Download URL decimo-0.16.0.dev20261009221123-cp313-cp313-manylinux_2_35_x86_64.whl
Size 1.6 MB
Tags CPython 3.13 Linux glibc 2.35+ x86-64
SHA-256 checksum
How to use checksums
a956d76dd6a89e82929ac45a67f437248cf0d0f8372639d218c37132de63cec2
BLAKE2b-256 checksum
How to use checksums
b7103f4bdf88a825a308c3b8dbc7eb18b34bd846fb9ec5dbd8c12ca838c00662
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 9, 2026.

Transparency log

Release files / decimo-0.16.0.dev20261009221123-cp313-cp313-manylinux_2_35_aarch64.whl

Download URL decimo-0.16.0.dev20261009221123-cp313-cp313-manylinux_2_35_aarch64.whl
Size 1.4 MB
Tags CPython 3.13 Linux glibc 2.35+ ARM64
SHA-256 checksum
How to use checksums
7ba680023c7ca58fe1be35efc0390e0e803e181b492cafe58d2ac71fdc900b5c
BLAKE2b-256 checksum
How to use checksums
e15493cca04750c944f35bb2fcfd8ce1653b6f7a70e02bc4f573af0a9620c83c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 9, 2026.

Transparency log

Release files / decimo-0.16.0.dev20261009221123-cp313-cp313-macosx_11_0_arm64.whl

Download URL decimo-0.16.0.dev20261009221123-cp313-cp313-macosx_11_0_arm64.whl
Size 1.3 MB
Tags CPython 3.13 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
fcbd5ac2879edd05c0b382434d5ee9258f5f8c8e3992700605934613223e4e0c
BLAKE2b-256 checksum
How to use checksums
da1ea00121dc9eae7c90107e723a3452c8702da1eb9fc525e6ee94679bc2790e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 9, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.15.0

6 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page