GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Fraction wrongfully gets casted into float when given as argument to __rpow__ · Issue #119189 · python/cpython

Fraction wrongfully gets casted into float when given as argument to __rpow__ #119189

New issue
Copy link
New issue
Copy link
Closed
Closed
Fraction wrongfully gets casted into float when given as argument to __rpow__#119189
Copy link
Labels
type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

Description

@retooth2
retooth2
opened on May 19, 2024
Issue body actions

Bug report

Bug description:

When using the ** operator with a Fraction as a base and an object that implements __rpow__ as an exponent the fraction gets wrongfully casted into a float before being passed to __rpow__

from fractions import Fraction

foo = Fraction(4, 3)

class One:
    def __rpow__(self, other):
        return other

bar = foo**One()
print(bar)
print(type(bar))

Expected Output

4/3
<class 'fractions.Fraction'>

Actual Output:

1.3333333333333333
<class 'float'>

Tested with Python 3.12.3

CPython versions tested on:

3.12

Operating systems tested on:

macOS

Linked PRs

  • gh-119189: Add more tests for mixed Fraction arithmetic #119236
  • gh-119189: Fix the fraction module so that __rpow__ works on arbitrary classes. #119242
  • [3.13] gh-119189: Add more tests for mixed Fraction arithmetic (GH-119236) #119255
  • [3.12] gh-119189: Add more tests for mixed Fraction arithmetic (GH-119236) #119256
  • gh-119189: Add yet more tests for mixed Fraction arithmetic #119298
  • [3.13] gh-119189: Add yet more tests for mixed Fraction arithmetic (GH-119298) #119346
  • [3.12] gh-119189: Add yet more tests for mixed Fraction arithmetic (GH-119298) #119347
  • [3.12] gh-119189: Fix the power operator for Fraction (GH-119242) #119835
  • [3.13] gh-119189: Fix the power operator for Fraction (GH-119242) #119836

Activity

Next
  1. retooth2
    added
    type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
    on May 19, 2024
  2. zitterbewegung commented on May 20, 2024

    @zitterbewegung
    zitterbewegung
    on May 20, 2024
    Contributor
    More actions

    I'm investigating this.

  3. zitterbewegung commented on May 20, 2024

    @zitterbewegung
    zitterbewegung
    on May 20, 2024
    Contributor
    More actions

    Reproduced under Linux (Ubuntu). I am working on a Fix (I'm in the sprint workshop)

  4. MojoVampire commented on May 20, 2024

    @MojoVampire
    MojoVampire
    on May 20, 2024
    Contributor
    More actions

    It's a mistake in the implementation of Fraction.__pow__; rather than returning NotImplemented when the type of the exponent is unrecognized and letting the other type handle it through __rpow__ normally, it intentionally does return float(a) ** b (where a is the name given to self).

    It does something similar when a Fraction is raised to the power of another non-one denominator Rational (return float(a) ** float(b)), but it at least makes more sense there (the comments note that raising a fraction to a fraction generally produces an irrational number, which can't be represented as a fraction in the first place).

  5. MojoVampire commented on May 20, 2024

    @MojoVampire
    MojoVampire
    on May 20, 2024
    Contributor
    More actions

    Is anyone working on a fix? If not, this is an easy one, I can do it.

    I worry slightly about possible breakage of existing code, but:

    1. The existing behavior is a clear break from the operator overloading rules, and not one justified by anything special about fractions (unlike the fraction to fractional power case)
    2. The existing behavior loses data, while the fix would not, so if the existing behavior is desired, an __rpow__ provider can always revert to the old behavior manually (they don't even need it to be version-specific; if they detect a Fraction, which can't be given right now, they convert to float; on older Python, they'll never see a Fraction in the first place)
    3. I can't imagine many cases where this code path is exercised; if you're writing a type designed to work with numbers like this, it's almost always going to be part of the numeric tower, and should be able to handle other layers of the numeric tower appropriately.
  6. zitterbewegung commented on May 20, 2024

    @zitterbewegung
    zitterbewegung
    on May 20, 2024
    Contributor
    More actions

    @MojoVampire I am working on a fix I'm in the cpython sprint.

  7. MojoVampire commented on May 20, 2024

    @MojoVampire
    MojoVampire
    on May 20, 2024
    Contributor
    More actions

    @zitterbewegung: Same. I'm the guy standing up cause my back is borked. Fix is pretty easy:

            elif isinstance(b, (float, complex)):
                return float(a) ** b
            else:
                return NotImplemented
    

    replacing the existing final line. Tests are always the pain. I'll review when you're done.

  8. retooth2 commented on May 20, 2024

    @retooth2
    retooth2
    on May 20, 2024
    Author
    More actions
    1. I can't imagine many cases where this code path is exercised; if you're writing a type designed to work with numbers like this, it's almost always going to be part of the numeric tower, and should be able to handle other layers of the numeric tower appropriately.

    For context: I stumbled over the bug when implementing a wrapper number class for exact precision arithmetic that holds the data as a symbolic expression (so e.g. irrational numbers can be used in calculations without error). The class is designed to work with all builtin number types, e.g.

    a = Fraction(1, 3)
    b = ExactNumber(Fraction(1, 2))
    print(a**b)
    ExactNumber(1/3**1/2)
    

    My class is built on the sympy package that does similar things on its own, so this should also be relevant to them.
    Anyway: Thanks for the quick response, I really appreciate it.

    Best,
    Fabian

  9. serhiy-storchaka
    added a commit that references this issue on May 20, 2024

    pythongh-119189: Add more tests for mixed Fraction arithmetic

    317c1c6
  10. serhiy-storchaka commented on May 20, 2024

    @serhiy-storchaka
    serhiy-storchaka
    on May 20, 2024
    Member
    More actions

    I worked on this today. The issue itself may be not difficult, but I noticed that tests do not cover many cases in mixed Fraction arithmetic. #119236 adds many new tests, so we will see what other side effect our changes can cause.

  11. zitterbewegung commented on May 20, 2024

    @zitterbewegung
    zitterbewegung
    on May 20, 2024
    Contributor
    More actions

    @serhiy-storchaka I will look at this with @MojoVampire to see if there are any additional issues.

  12. MojoVampire commented on May 20, 2024

    @MojoVampire
    MojoVampire
    on May 20, 2024
    Contributor
    More actions

    I'd suggest the news entry simplify to something like:

    fractions.Fraction.__pow__ no longer coerces itself to float for unrecognized exponent types, instead returning NotImplemented to defer to the exponent's __rpow__ as normal.

    Keeps it focused on what was fixed.

  13. zitterbewegung commented on May 20, 2024

    @zitterbewegung
    zitterbewegung
    on May 20, 2024
    Contributor
    More actions

    With this fix I see a test failure when manually putting in the new test_fractions code in
    #119236

    ======================================================================
    FAIL: testMixedPower (test.test_fractions.FractionTest.testMixedPower)

    Traceback (most recent call last):
    File "/Users/r2q2/Projects/cpython/Lib/test/test_fractions.py", line 903, in testMixedPower
    self.assertTypedEquals(F(3, 2) ** Rect(2, 0), Polar(2.25, 0.0))
    ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    File "/Users/r2q2/Projects/cpython/Lib/test/test_fractions.py", line 282, in assertTypedEquals
    self.assertEqual(expected, actual)
    ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
    AssertionError: Polar(Fraction(9, 4), 0.0) != Polar(2.25, 0.0)


    Ran 45 tests in 0.038s

    FAILED (failures=1)
    test test_fractions failed
    test_fractions failed (1 failure)

    == Tests result: FAILURE ==

    1 test failed:
    test_fractions

    Total duration: 97 ms
    Total tests: run=45 failures=1
    Total test files: run=1/1 failed=1
    Result: FAILURE

  14. MojoVampire commented on May 20, 2024

    @MojoVampire
    MojoVampire
    on May 20, 2024
    Contributor
    More actions

    Looks like a mistake in the test (or if you prefer, a test tailored to the old behavior). The Rect defines:

        def __rpow__(self, other):
            return Polar(other ** self.x, math.log(other) * self.y)
    

    and the test expects other ** self.x to produce a float, but given other is a Fraction, and self.x is an int (which Fraction can use losslessly to produce a new Fraction), the observed result is correct, post-patch.

  15. serhiy-storchaka
    added a commit that references this issue on May 20, 2024

    gh-119189: Add more tests for mixed Fraction arithmetic (GH-119236)

    fe67af1
  16. 6 remaining items

  17. miss-islington
    added 2 commits that reference this issue on May 21, 2024

    pythongh-119189: Add yet more tests for mixed Fraction arithmetic (py…

    c0e8fe8

    pythongh-119189: Add yet more tests for mixed Fraction arithmetic (py…

    7ffcf54
  18. serhiy-storchaka
    added a commit that references this issue on May 21, 2024

    [3.12] gh-119189: Add yet more tests for mixed Fraction arithmetic (G…

    47b09d2
  19. serhiy-storchaka commented on May 22, 2024

    @serhiy-storchaka
    serhiy-storchaka
    on May 22, 2024
    Member
    More actions

    The new tests were purposed to fail with this change. They were added so that we can see what effect the change has and decide whether it is correct. If some visible changes are not correct, we should find other fixes for such cases.

    I have other changes for mixed Fraction arithmetic, but I am not sure that they should be backported, so I'll create a separate PR.

  20. serhiy-storchaka
    added a commit that references this issue on May 22, 2024

    [3.13] gh-119189: Add yet more tests for mixed Fraction arithmetic (G…

    ec484b6
  21. serhiy-storchaka
    added a commit that references this issue on May 31, 2024

    gh-119189: Fix the power operator for Fraction (GH-119242)

    b9965ef
  22. miss-islington
    added 2 commits that reference this issue on May 31, 2024

    pythongh-119189: Fix the power operator for Fraction (pythonGH-119242)

    490062a

    pythongh-119189: Fix the power operator for Fraction (pythonGH-119242)

    e895b00
  23. serhiy-storchaka commented on May 31, 2024

    @serhiy-storchaka
    serhiy-storchaka
    on May 31, 2024
    Member
    More actions

    See also #119838.

  24. serhiy-storchaka
    added 2 commits that reference this issue on Jul 16, 2024

    [3.12] gh-119189: Fix the power operator for Fraction (GH-119242) (GH…

    d4f57cc

    [3.13] gh-119189: Fix the power operator for Fraction (GH-119242) (GH…

    d02adec
  25. serhiy-storchaka
    closed this as completedon Jul 16, 2024
  26. estyxx
    added 3 commits that reference this issue on Jul 17, 2024

    pythongh-119189: Add more tests for mixed Fraction arithmetic (python…

    e065d35

    pythongh-119189: Add yet more tests for mixed Fraction arithmetic (py…

    de7bcf9

    pythongh-119189: Fix the power operator for Fraction (pythonGH-119242)

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      You are visitor 01402503 — GeezerVison does not store the pages you look up.
      Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.