Complementary residue arithmetic — 0.1 + 0.2 = 0.3, exactly.
Project description
MöbiusNumber
0.1 + 0.2 = 0.3. Exactly.
from mobius_number import M
>>> 0.1 + 0.2 == 0.3
False
>>> M("0.1") + M("0.2") == M("0.3")
True
A number type that carries its own correction. No rounding error. No epsilon comparisons. No workarounds.
Install
pip install mobius-number
The Problem
Every computer in the world gets this wrong:
>>> 0.1 + 0.2
0.30000000000000004
>>> 0.1 + 0.2 == 0.3
False
This has been true since 1985 when IEEE 754 was published. The reason: computers store numbers in base 2. The number 0.1 in binary is a repeating fraction — like 1/3 in decimal (0.333...). It gets rounded. Then 0.2 gets rounded. The rounding errors stack.
Every workaround — arbitrary precision, interval arithmetic, posit numbers — tries to fix the math layer while leaving the foundation untouched: the number is stored in the form the transistor speaks, and the human adapts.
What if the representation served the number instead?
The Idea
DNA has two strands. Every base carries its complement — A pairs with T, G pairs with C. There is no excess. The complement consumes what the original doesn't cover.
A Möbius strip has one surface. Traversing the full loop covers both "sides" and returns to the origin.
A MöbiusNumber stores two strands:
- The binary strand —
float64, hardware-fast, carries rounding error - The rational strand — exact
Fraction, no loss, no repeating
They are not two separate representations. They are one object. The binary strand is the shadow. The rational strand is the substance. On collapse, the rational governs.
CURRENT (IEEE 754):
Store 0.1 → 0.1000000000000000055511...
Residue: 0.0000000000000000055511... ← THROWN AWAY
MöbiusNumber:
Binary strand: 0.1000000000000000055511...
Rational strand: 1/10
Anti (residue): exact_value − binary_value
Collapse: the rational governs → 0.1 exactly
Arithmetic propagates both strands. When you add two MöbiusNumbers, the rationals add exactly. The floats add approximately. The error exists but is never consulted for truth. The anti-strand is always present, always correct, and annihilates the rounding error on collapse.
Proof
$ python -c "from mobius_number import M; print(M('0.1') + M('0.2') == M('0.3'))"
True
The Famous Failures — All Fixed
| Test | IEEE 754 | MöbiusNumber |
|---|---|---|
0.1 + 0.2 == 0.3 |
False | True |
(1/49) * 49 == 1 |
False | True |
(1 + 1e-16) - 1 |
0.0 (total loss) | 1e-16 (exact) |
0.1 * 10 == 1.0 |
True¹ | True |
$10 / 3 * 3 == $10 |
True¹ | True |
0.01 * 100 == 1.0 |
True¹ | True |
(1/7) * 7 == 1 |
False | True |
0.001 added 1000× |
False | True |
¹ IEEE 754 gets these by luck — the rounding errors happen to cancel. The MöbiusNumber gets them by construction.
Strand Anatomy
from mobius_number import M
n = M("0.1")
print(n.diagnose())
# {
# 'binary_strand': 0.1,
# 'rational_strand': '1/10',
# 'residue': '-1/180143985094819840',
# 'residue_float': -5.55e-18,
# 'collapsed': 0.1
# }
The residue is the anti-strand — the exact complement of the binary error. It exists. It is never discarded. On collapse, the Möbius strip closes.
Usage
from mobius_number import M
# Basic arithmetic
a = M("0.1")
b = M("0.2")
c = a + b # M('3/10')
c.collapse() # 0.3
# Financial
price = M("19.99")
tax = price * M("0.0825")
total = price + tax # Exact
# Comparison — the rational governs
M("0.1") + M("0.2") == M("0.3") # True — always
# Interop with plain numbers
M("0.5") + 1 # M('3/2')
3 * M("0.1") # M('3/10')
# Inspect the strands
n = M("0.1")
n.approx # 0.1 (the float — fast, lossy)
n.exact # Fraction(1, 10) (the truth)
n.residue # Fraction(-1, 180143985094819840)
Why Not Just Use Fraction?
You can. Python's fractions.Fraction gives exact rational arithmetic. But:
- Speed — MöbiusNumber carries a float for fast approximate work. Use
.approxin hot loops,.collapse()when you need truth. - Drop-in intent —
M("0.1")reads like a number.Fraction("0.1")reads like a workaround. - The conceptual point — the number and its correction are one object. The anti-strand is not a separate operation. It is intrinsic. Like DNA. Like a Möbius strip.
How It Works
Every MöbiusNumber is internally:
(_approx: float, _exact: Fraction)
- Construction from string:
M("0.1")→_exact = Fraction("0.1") = 1/10;_approx = float(1/10) - Construction from float:
M(0.1)→ recovers the rational intent vialimit_denominator - Arithmetic: both strands propagate independently through
+,-,*,/,** - Comparison: always uses
_exact— the rational strand governs all equality and ordering - Collapse:
float(_exact)— the Möbius traversal returns the exact value
No external dependencies. Pure Python. Works on 3.9+.
The Name
A Möbius strip is a surface with one side. If you trace a line along it, you cover both "sides" and return to the origin having traversed the whole thing. There is no front and back — only one continuous surface.
A MöbiusNumber is a number with one identity. The binary approximation and the exact rational are not two things — they are one object that, when fully traversed, resolves to the truth. The representation IS the correction.
Author
Jay Carpenter — SECS Research
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mobius_number-0.1.0.tar.gz.
File metadata
- Download URL: mobius_number-0.1.0.tar.gz
- Upload date:
- Size: 8.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3996c17c49cf3467cfe729e8a7baaea4ed65fabbcdc35078ce0e5ce3fe59ba7a
|
|
| MD5 |
71ea38d4df44b5675104f1105299b950
|
|
| BLAKE2b-256 |
4e6397d19c43a379a80228a66b35669b59b256cf9fd4239b4e86f29a39bc0086
|
File details
Details for the file mobius_number-0.1.0-py3-none-any.whl.
File metadata
- Download URL: mobius_number-0.1.0-py3-none-any.whl
- Upload date:
- Size: 6.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3ef641a7eb4c553c81e62fbb99492a55917135773e69357dcc1c4febdb36121b
|
|
| MD5 |
007c7a3c497caf0b1eac7bd6fdd4c26a
|
|
| BLAKE2b-256 |
cd8506f46a2d29e867d5f9a42bbe7af05fc1009c08647e47e75655dfc5f25d32
|