The simplefractions package finds the simplest fraction that converts to a given float, or more generally, the simplest fraction that lies within a given interval.
Definition. Given fractions x = a/b and y = c/d (written in lowest terms with positive denominators), say that x is simpler than y if abs(a) <= abs(c), b <= d, and at least one of these two inequalities is strict.
For example, 22/7 is simpler than 23/8, but neither of 3/8 and 4/7 is simpler than the other.
Then it's a theorem that given any subinterval I of the real line that contains at least one fraction, that interval contains a unique simplest fraction. That is, there's a fraction a/b in I such that a/b is simpler (in the above sense) than all other fractions in I. As a consequence, for any given finite Python float x, there's a unique simplest fraction that rounds to that float.
The simplefractions package provides two functions:
simplest_from_floatreturns, for a given floatx, the unique simplest fraction with the property thatfloat(simplest_from_float(x)) == x.simplest_in_intervalreturns the unique simplest fraction in a given (open or closed, bounded or unbounded) nonempty interval.
Example usage
Start by importing the functions from the module:
>>> from simplefractions import *
The simplest_from_float function takes a single finite float x and
produces a Fraction object that recovers that float:
>>> simplest_from_float(0.25)
Fraction(1, 4)
>>> simplest_from_float(0.33)
Fraction(33, 100)
No matter what x is given, the invariant float(simplest_from_float(x)) == x
will always be true.
>>> x = 0.7429667872099244
>>> simplest_from_float(x)
Fraction(88650459, 119319545)
>>> float(simplest_from_float(x))
0.7429667872099244
>>> float(simplest_from_float(x)) == x
True
If the float x was constructed by dividing two small integers, then
more often than not, simplest_from_float will recover those integers:
>>> x = 231 / 199
>>> x
1.1608040201005025
>>> simplest_from_float(x)
Fraction(231, 199)
More precisely, if x was constructed by dividing two relatively prime
integers smaller than or equal to 67114657 in absolute value,
simplest_from_float will recover those integers.
>>> simplest_from_float(64841043 / 66055498)
Fraction(64841043, 66055498)
But 67114657 is the best we can do here:
>>> simplest_from_float(67114658 / 67114657)
Fraction(67114657, 67114656)
In larger cases, simplest_from_float might discover a simpler fraction
that gives the same float:
>>> x = 818421477165 / 1580973145504
>>> simplest_from_float(x)
Fraction(5171, 9989)
>>> 818421477165 / 1580973145504 == 5171 / 9989
True
Note that simplest_from_float does not magically fix floating-point
inaccuracies. For example:
>>> x = 1.1 + 2.2
>>> simplest_from_float(x)
Fraction(675539944105597, 204709073971393)
You might have expected Fraction(33, 10) here, but when converted to float,
that gives a value very close to, but not exactly equal to, x. In contrast,
the return value of simplest_from_float(x) will always produce exactly x
when converted to float.
To fix this, you might want to ask for the simplest float that lies within
some small error bound of x - for example, within 5 ulps (units in the
last place) in either direction. simplest_from_float can't do that, but
simplest_in_interval can! For example
>>> from math import ulp
>>> x = 1.1 + 2.2
>>> simplest_in_interval(x - 5*ulp(x), x + 5*ulp(x))
Fraction(33, 10)
Alternatively, you might ask for the simplest fraction approximating x
with a relative error of at most 0.000001:
>>> relerr = 1e-6
>>> simplest_in_interval(x - relerr*x, x + relerr*x)
Fraction(33, 10)
Here are some more examples of simplest_in_interval at work. The inputs
to simplest_in_interval can be floats, integers, or Fraction objects.
>>> simplest_in_interval(3.14, 3.15)
Fraction(22, 7)
By default, simplest_in_interval assumes that you're specifying an
open interval:
>>> simplest_in_interval(3, 4)
Fraction(7, 2)
Keyword arguments include_left and include_right allow you to specify
that one or both endpoints should be included in the interval:
>>> simplest_in_interval(3, 4, include_left=True, include_right=True)
Fraction(3, 1)
The left and right endpoints of the interval are also both optional, alowing a semi-infinite or infinite interval to be specified:
>>> simplest_in_interval(right=4) # simplest in (-inf, 4)
Fraction(0, 1)
>>> simplest_in_interval(left=4, include_left=True) # simplest in [4, inf)
Fraction(4, 1)
>>> simplest_in_interval() # simplest in (-inf, inf)
Fraction(0, 1)
Metadata
Release files for simplefractions 1.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| simplefractions-1.4.0.tar.gz | 8.2 kB | Without provenance | Details for simplefractions-1.4.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| simplefractions-1.4.0-py3-none-any.whl | Python 3 | none | any | Details for simplefractions-1.4.0-py3-none-any.whl |
Total release size: 19.1 kB
Release files / simplefractions-1.4.0.tar.gz
| Download URL | simplefractions-1.4.0.tar.gz |
|---|---|
| Size | 8.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6959df8522013fe3052efa82bf91bb47bc50a0b04ec8e718efc36ed6fbac9e21
|
|
BLAKE2b-256 checksum How to use checksums |
6394938c0dde8fe9b5248a41eec800373bb332081417e8ba45cd51f83b2eb55b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.9.7
|
Release files / simplefractions-1.4.0-py3-none-any.whl
| Download URL | simplefractions-1.4.0-py3-none-any.whl |
|---|---|
| Size | 10.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
01041d607201b02bf8149767be3deb5cb9c789eef1cbef051b9fd7325b866988
|
|
BLAKE2b-256 checksum How to use checksums |
da7b8bc029826c6ce8c3cb339122dbc0f524e33db432843d4ae9ed8c2d88d2e9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.9.7
|
Security for simplefractions 1.4.0
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.