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.

IFDRational delegates __eq__, __int__, __round__, __repr__ and arithmetic to
self._val (the normalized Fraction), but never defined __float__. For a
non-integral numerator (e.g. IFDRational(1.5, 3), whose value is 0.5) float()
fell back to numbers.Rational.__float__, which computes
int(numerator) / int(denominator) = int(1.5) / int(3) = 0.333..., disagreeing
with ==, int() and repr() (all 0.5).

Delegate __float__ to self._val so float() is consistent with the rest of the
class.
Comment thread Tests/test_tiff_ifdrational.py Outdated
Comment thread Tests/test_tiff_ifdrational.py Outdated
@radarhere radarhere changed the title Fix IFDRational.__float__ returning the wrong value Correct IFDRational.__float__() return value Jun 17, 2026
Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com>
@mergify

mergify Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

@hugovk hugovk merged commit be0b7ee into python-pillow:main Jun 19, 2026
53 checks passed
@radarhere radarhere changed the title Correct IFDRational.__float__() return value Correct IFDRational.__float__() return value Jun 20, 2026
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