# Improve support for unnormalized Fractions

**URL:** <https://discuss.python.org/t/improve-support-for-unnormalized-fractions/24013>\
**Category:** Ideas\
**Created:** [February 19, 2023, 2:03pm UTC](https://discuss.python.org/t/improve-support-for-unnormalized-fractions/24013 "2023-02-19T14:03:04Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![steven.daprano](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/steven.daprano/32/1083_2.png) [@steven.daprano](https://discuss.python.org/u/steven.daprano)\
**Post date:** [February 19, 2023, 2:03pm UTC](https://discuss.python.org/t/improve-support-for-unnormalized-fractions/24013/1 "2023-02-19T14:03:04Z")

</div>

I’ve wanted a way to create fractions like 2/6 which haven’t been reduced to lowest form for the longest time, and today I learned that `Fraction` already supports this!

`Fraction` has a lovely little hidden feature: by passing a private keyword argument to the constructor, we can create _unnormalized fractions_ where the numerator and denominator are not reduced to lowest form:

```auto
>>> from fractions import Fraction
>>> a = Fraction(2, 6) # By default, fractions are normalised to lowest form.
>>> b = Fraction(2, 6, _normalize=False)
>>> print(a, b)
1/3 2/6

```

This can occasionally be useful:

- Ratios and proportions.
- When working with fractions without reduction to lowest term, e.g. for primary school fractions.
- For [mediants](https://en.wikipedia.org/wiki/Mediant_%28mathematics%29).

I was surprised to learn that there is no obvious supported way to then normalise such a fraction. There is no `normalize()` or `reduce()` method. I thought that the unary `+` sign might work, but it doesn’t.

Let’s improve handling of such unnormalised fractions:

- Make unnormalised fractions official, by promoting the private `_normalize` parameter to public, or by adding a new alternate constructor.
- Add support to normalise fractions to lowest form, I suggest the unary `+` operator.

Thoughts?

---

<div class="post-metadata">

**Author:** ![mdickinson](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/mdickinson/32/976_2.png) [@mdickinson](https://discuss.python.org/u/mdickinson)\
**Post date:** [February 19, 2023, 2:57pm UTC](https://discuss.python.org/t/improve-support-for-unnormalized-fractions/24013/2 "2023-02-19T14:57:11Z")

</div>

I’m afraid that no, `Fraction` does not already support this. That’s not what the `_normalize` keyword is for. It’s for situations where you already know that the numerator and denominator you’re supplying are in lowest terms (with positive denominator) and you want to skip the expensive extra `gcd` computation that would otherwise be required to normalise.

Some of the various methods on `Fraction` require the fraction to be normalised in order to produce correct results. `limit_denominator` is one example, equality checks are another, but there are almost certainly others. Bits of it may happen to “work” with unnormalised fractions, but if they do then that’s by accident rather than by design.

---

<div class="post-metadata">

**Author:** ![mdickinson](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/mdickinson/32/976_2.png) [@mdickinson](https://discuss.python.org/u/mdickinson)\
**Post date:** [February 19, 2023, 3:07pm UTC](https://discuss.python.org/t/improve-support-for-unnormalized-fractions/24013/3 "2023-02-19T15:07:16Z")

</div>

Meta: this is a beautiful example of [Hyrum’s law](https://www.hyrumslaw.com) (see also [xkcd: Workflow](https://xkcd.com/1172/)) in action, and the reason why changes like that proposed in [gh-101773: Optimize creation of Fraction's in private methods by skirpichev · Pull Request #101780 · python/cpython · GitHub](https://github.com/python/cpython/pull/101780) are potentially dangerous.

---

<div class="post-metadata">

**Author:** ![Rosuav](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/rosuav/32/3429_2.png) [@Rosuav](https://discuss.python.org/u/Rosuav)\
**Post date:** [February 19, 2023, 3:16pm UTC](https://discuss.python.org/t/improve-support-for-unnormalized-fractions/24013/4 "2023-02-19T15:16:35Z")

</div>

> [@steven.daprano](#):
>
> I’ve wanted a way to create fractions like 2/6 which haven’t been reduced to lowest form for the longest time

What operations would you do on a non-reduced fraction? Would it be practical to spin up a class that looks broadly like this:

```auto
class AbnormalFraction: # or denormal or unnormal or whatever
    def __init__ (self, n, d):
        self.numerator = n; self.denominator = d
    def __add__ (self, other):
        return Fraction(self) + other
    def __mul__ (self, other):
        return AbnormalFraction(self.numerator * other, self.denominator)
# etc

```

where every operation you’re interested in is implemented either directly, or on top of fractions.Fraction?

---

<div class="post-metadata">

**Author:** ![storchaka](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/storchaka/32/217_2.png) [@storchaka](https://discuss.python.org/u/storchaka)\
**Post date:** [February 19, 2023, 3:32pm UTC](https://discuss.python.org/t/improve-support-for-unnormalized-fractions/24013/5 "2023-02-19T15:32:31Z")

</div>

No, it does not work.

```auto
>>> from fractions import Fraction
>>> a = Fraction(2, 6) # By default, fractions are normalised to lowest form.
>>> b = Fraction(2, 6, _normalize=False)
>>> a == b
False
>>> b = Fraction(12, 6, _normalize=False)
>>> b.is_integer()
False
>>> b = Fraction(2, -6, _normalize=False)
>>> b < 0
False

```

The math of unnormalized fractions is weird. If you want to get some reasonable results you need to add an additional code and slow down normalized fractions. I am not even sure that that there is a non-contradictional system.

---

<div class="post-metadata">

**Author:** ![skirpichev](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/skirpichev/32/10996_2.png) [@skirpichev](https://discuss.python.org/u/skirpichev)\
**Post date:** [February 27, 2023, 11:09am UTC](https://discuss.python.org/t/improve-support-for-unnormalized-fractions/24013/6 "2023-02-27T11:09:28Z")

</div>

> [@storchaka](#):
>
> If you want to get some reasonable results you need to add an additional code and slow down normalized fractions.

I think it should be possible without too extra overhead with an additional sticky flag (e.g. is\_normalized). If it’s true - go for a quick path in equality/relational ops and so on. If not - proceed with an unnormalized version.  
But I doubt the proposed feature does make sense for the Fraction class. In fact, probably every method should be adapted in the above way, even arithmetic methods, that “works” now. That’s because current arithmetic methods actually _do_ a partial normalization, e.g.:

```auto
>>> a, b = Q(2, 4, _normalize=False), Q(-4, -8, _normalize=False)
>>> a+b
Fraction(-2, -2)

```

instead of `-32/-32`.  
So, probably this should be a new alternate class for fractions. Not sure it worth.
