Skip to content

Correct IFDRational.__float__() return value - #9676

Merged
hugovk merged 2 commits into
python-pillow:mainfrom
nyxst4ck:fix/ifdrational-float
Jun 19, 2026
Merged

Correct IFDRational.__float__() return value#9676
hugovk merged 2 commits into
python-pillow:mainfrom
nyxst4ck:fix/ifdrational-float

Conversation

@nyxst4ck

@nyxst4ck nyxst4ck commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Changes proposed

IFDRational.__float__ returns the wrong value for a non-integral numerator.

IFDRational delegates __eq__, __int__, __round__, __repr__ and all arithmetic to self._val (the normalized Fraction), but never defined __float__. So float() falls back to numbers.Rational.__float__, which does int(self.numerator) / int(self.denominator). For a non-integral numerator the stored _numerator/_denominator are un-normalized, so the result disagrees with every other accessor:

>>> from PIL.TiffImagePlugin import IFDRational
>>> r = IFDRational(1.5, 3)   # value 1.5 / 3 == 0.5
>>> r == 0.5
True
>>> int(r), repr(r)
(0, '0.5')
>>> float(r)
0.3333333333333333          # int(1.5) / int(3) == 1/3, should be 0.5

This is reachable via the public IFDRational(value, denominator) constructor.

Fix

Delegate __float__ to self._val, the same way __int__, __round__, __repr__ and __eq__ are delegated, so float() is consistent with the normalized fraction.

Tests

Added test_float to Tests/test_tiff_ifdrational.py: it asserts float(IFDRational(1.5, 3)) == 0.5, and that the integral and 0/0 (nan) cases stay correct. It fails on main (0.333… != 0.5) and passes with the change; Tests/test_tiff_ifdrational.py is green (6 passed). ruff and black clean.

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants