fracpylib
fracpylib is a small, dependency-free Python library for exact fraction arithmetic, mixed fractions, and recurring decimal conversion.
It keeps fractions normalized, supports natural operators, and can round-trip values such as 1.1(6) without losing precision to floats.
Contents
- Highlights
- Installation
- Quick start
- Fraction API
- Mixed fractions
- Periodic decimals
- Utilities
- Development
- License
Highlights
| Feature | Example |
|---|---|
| Canonical immutable fractions | Fraction(6, -8) becomes Fraction(-3, 4) |
| Integer-friendly operators | 1 + Fraction(1, 2) == Fraction(3, 2) |
| Mixed fractions | MixedFraction(1, 5, 4) becomes 2 1/4 |
| Periodic decimal parsing | Fraction.from_decimal("0.(09)") == Fraction(1, 11) |
| Periodic decimal rendering | Fraction(1, 6).to_periodic() == "0.1(6)" |
| Decimal utility checks | FracMisc.is_terminating(Fraction(1, 8)) |
Installation
pip install fracpylib
Quick Start
from fracpylib.fractionlib import Fraction, MixedFraction
a = Fraction(2, 4)
b = Fraction.from_decimal("1.1(6)")
print(a) # 1/2
print(b) # 7/6
print(a + b) # 5/3
print(2 * a) # 1
print(float(b)) # 1.1666666666666667
mixed = MixedFraction(1, 5, 4)
print(mixed) # 2 1/4
print(mixed.to_fraction()) # 9/4
Fraction API
Fraction stores every value in reduced form with a positive denominator. Values are immutable after construction, so equality, hashing, string output, and comparisons all use the same canonical representation.
from fracpylib.fractionlib import Fraction
x = Fraction(4, 8)
y = Fraction(3, 4)
assert x.as_tuple() == (1, 2)
assert x + y == Fraction(5, 4)
assert x - 1 == Fraction(-1, 2)
assert x * y == Fraction(3, 8)
assert x / y == Fraction(2, 3)
assert x ** -2 == Fraction(4, 1)
Helpful methods:
from fracpylib.fractionlib import Fraction, MixedFraction
half = Fraction(1, 2)
assert half.reciprocal() == Fraction(2, 1)
assert half.to_mixed() == MixedFraction(0, 1, 2)
assert half.to_periodic() == "0.5"
Mixed Fractions
MixedFraction accepts whole, numerator, and denominator pieces, then normalizes itself through an improper Fraction. Pass the sign on the whole part and keep the fractional numerator non-negative.
from fracpylib.fractionlib import Fraction, MixedFraction
mf = MixedFraction(1, 5, 4)
assert str(mf) == "2 1/4"
assert mf.to_fraction() == Fraction(9, 4)
assert mf + Fraction(1, 4) == Fraction(5, 2)
assert 3 - mf == Fraction(3, 4)
Negative values are normalized too:
assert str(MixedFraction.from_fraction(Fraction(-3, 2))) == "-1 1/2"
assert str(MixedFraction.from_fraction(Fraction(-1, 2))) == "-1/2"
Periodic Decimals
Use Fraction.from_decimal() when you already have a decimal string. Repeating digits go in parentheses.
from decimal import Decimal
from fracpylib.fractionlib import Fraction
assert Fraction.from_decimal("0.(3)") == Fraction(1, 3)
assert Fraction.from_decimal("1.1(6)") == Fraction(7, 6)
assert Fraction.from_decimal("-0.1(6)") == Fraction(-1, 6)
assert Fraction.from_decimal(".125") == Fraction(1, 8)
assert Fraction.from_decimal(Decimal("1.20")) == Fraction(6, 5)
Use Fraction.from_periodic() when the whole, non-repeating, and repeating parts are already separated:
assert Fraction.from_periodic(whole=1, non_repeating="1", repeating="6") == Fraction(7, 6)
assert Fraction.from_periodic(whole=0, non_repeating="1", repeating="6", sign=-1) == Fraction(-1, 6)
Render exact decimals with to_periodic():
assert Fraction(1, 2).to_periodic() == "0.5"
assert Fraction(1, 3).to_periodic() == "0.(3)"
assert Fraction(1, 6).to_periodic() == "0.1(6)"
assert Fraction(-22, 7).to_periodic() == "-3.(142857)"
Utilities
FracMath exposes arithmetic helpers for users who prefer function calls over operators. FracMisc contains common fraction predicates.
from fracpylib.fractionlib import Fraction, FracMath, FracMisc
assert FracMath.add(Fraction(1, 2), Fraction(1, 3)) == Fraction(5, 6)
assert FracMisc.is_proper(Fraction(2, 3)) is True
assert FracMisc.is_improper(Fraction(3, 2)) is True
assert FracMisc.is_terminating(Fraction(1, 8)) is True
assert FracMisc.has_repeating_decimal(Fraction(1, 3)) is True
The old camelCase utility names are still available:
assert FracMisc.isProper(Fraction(2, 3)) is True
assert FracMisc.isImproper(Fraction(3, 2)) is True
Development
Create or activate a virtual environment, then run:
python -m pip install -e . pytest
python -m pytest -q
Build the package locally with:
python -m build
Publishing is automated by GitHub Actions on pushes to main and version tags. Add a repository secret named PYPI_API_TOKEN; the workflow checks the project files, runs hatch run test, then runs hatch run publish.
License
fracpylib is distributed under the terms of the MIT license.
find me on daily.dev:
Metadata
Release files for fracpylib 2.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| fracpylib-2.2.1.tar.gz | 12.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fracpylib-2.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 22.8 kB
Release files / fracpylib-2.2.1.tar.gz
| Download URL | fracpylib-2.2.1.tar.gz |
|---|---|
| Size | 12.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f4c41ce13acaee9d519a8d3ccf2d921009bc84e2095d6ae65d0ddc79a92a0378
|
|
BLAKE2b-256 checksum How to use checksums |
888f3c452bd63a4ded30fad320be1bb1bcd58abf420624e6d493a1ef9e15c5c8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|
Release files / fracpylib-2.2.1-py3-none-any.whl
| Download URL | fracpylib-2.2.1-py3-none-any.whl |
|---|---|
| Size | 10.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5efd9a4933bd0c384463762a2db725b001ea496b77d88d710dd09f1cde21dcfe
|
|
BLAKE2b-256 checksum How to use checksums |
9488cc23aa005a02dba8d1818e4adfa52fba55715a96fae2e4d1f262051b3c54
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|