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.
⚠️ Development release. The API is settled enough to use, but the versions carry a timestamp:
0.15.0.devYYYYMMDDHHMMSS, where0.15.0is 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().precandgetcontext().rounding,setcontext(),localcontext()(with keyword overrides),Contextobjects- 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()andto_integral_value() int(),float(),round(),math.floor/ceil/trunc,hash(),format()Contextyou 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
decimaltakes them:quantize(exp, rounding=ROUND_HALF_UP),to_integral_value(rounding=...),sqrt(context=...) Decimal((sign, digits, exponent)), soas_tuple()round-tripspow(x, y, modulus), by modular exponentiationquantize,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:
intconverts in arithmetic,floatdoes not, and both convert in a comparison copy,deepcopyandpickleZeroDivisionErrorwhere you expect it, and hashes that agree withint,floatanddecimal.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()andis_infinite()are alwaysFalse. ROUND_05UP. Nothing implements it; setting it raisesNotImplementedError.sqrt,exp,lnandlog10ignore the context rounding, as they do indecimal, and round half to even unless you passrounding=.**follows the context, also as indecimal. All of them are correctly rounded whichever mode applies, as are arithmetic,quantizeandround().//,%,divmod()andremainder_nearwith a long quotient.decimalraisesInvalidOperationwhen the integer quotient has more digits than the precision; decimo answers. The remainder is rounded to the context, as indecimal.Eminonly reaches four methods. Exponents are unbounded, soEminchanges nothing except whatnext_plus(0),next_minus(0),is_subnormal()andnumber_class()say, wheredecimal's answers need a smallest exponent.- Signals and traps.
Context.flagsandContext.trapsexist and stay empty.Inexact,Roundedand 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
Links
Metadata
Release files for decimo 0.16.0.dev20261009154242
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| decimo-0.16.0.dev20261009154242-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.dev20261009154242-cp314-cp314-manylinux_2_35_aarch64.whl | CPython 3.14 | CPython 3.14 | Linux glibc 2.35+ ARM64 | Details |
| decimo-0.16.0.dev20261009154242-cp314-cp314-macosx_11_0_arm64.whl | CPython 3.14 | CPython 3.14 | macOS 11.0+ ARM64 | Details |
| decimo-0.16.0.dev20261009154242-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.dev20261009154242-cp313-cp313-manylinux_2_35_aarch64.whl | CPython 3.13 | CPython 3.13 | Linux glibc 2.35+ ARM64 | Details |
| decimo-0.16.0.dev20261009154242-cp313-cp313-macosx_11_0_arm64.whl | CPython 3.13 | CPython 3.13 | macOS 11.0+ ARM64 | Details |
Total release size: 8.4 MB
Release files / decimo-0.16.0.dev20261009154242-cp314-cp314-manylinux_2_35_x86_64.whl
| Download URL | decimo-0.16.0.dev20261009154242-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 |
7d79c9f75956360d94a55bf01b3ffaf477cd6c3f3d089e64533e6fbd44191e9c
|
|
BLAKE2b-256 checksum How to use checksums |
82452601f03eeeddda010680e328ea3b537e1e1ae71836bff7c5b19592147a93
|
| 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 logRelease files / decimo-0.16.0.dev20261009154242-cp314-cp314-manylinux_2_35_aarch64.whl
| Download URL | decimo-0.16.0.dev20261009154242-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 |
78d181ac0398387e5db992dea9dd5e47f5f0407103de4aea26b5f13f2aef69f0
|
|
BLAKE2b-256 checksum How to use checksums |
01a1c5a5753785365f5d8a9fc1a39491c5e0de02fcffe0b22742b332a10cc90f
|
| 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 logRelease files / decimo-0.16.0.dev20261009154242-cp314-cp314-macosx_11_0_arm64.whl
| Download URL | decimo-0.16.0.dev20261009154242-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 |
faa9abf5a73076f15c0f0bf60ddafb9dc50dce1e584d241fd59aaee0e9522741
|
|
BLAKE2b-256 checksum How to use checksums |
3b1c996879176ea6b910a2c3d1739a00cb80857490de1134cf6c206ccdf22824
|
| 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 logRelease files / decimo-0.16.0.dev20261009154242-cp313-cp313-manylinux_2_35_x86_64.whl
| Download URL | decimo-0.16.0.dev20261009154242-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 |
7f0a33d4f865b7b88dfd26fe1c0a4fe09436db6ca42ead06fc32951504ac6545
|
|
BLAKE2b-256 checksum How to use checksums |
238c9154c966fc19d3402bc6cf4af62a8be498f0cbbfbd375f0612548d373b0f
|
| 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 logRelease files / decimo-0.16.0.dev20261009154242-cp313-cp313-manylinux_2_35_aarch64.whl
| Download URL | decimo-0.16.0.dev20261009154242-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 |
e73eedc6dee8f13e286f398036b01ad9a748a8e06af4e0754206e3bb4fbb1f6d
|
|
BLAKE2b-256 checksum How to use checksums |
1cb8819ec4dee6537137f3b5dee1f25194e503dbe5088e70fe802fa490c300f1
|
| 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 logRelease files / decimo-0.16.0.dev20261009154242-cp313-cp313-macosx_11_0_arm64.whl
| Download URL | decimo-0.16.0.dev20261009154242-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 |
037df522d0c9e8793f2b9284ba2240ef82efdc21e37adf01f4fe908520645a98
|
|
BLAKE2b-256 checksum How to use checksums |
1605df162c51fa8850558c4b12351871ed881e34279c59d557cb839dd647f3f1
|
| 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