Repository navigation
Add math.gcd() #66676
Description
Activity
fractions.gcd() is required for normalising numerator and denominator of the Fraction data type. Some speed improvements were applied to Fraction in bpo-22464, now the gcd() function takes up about half of the instantiation time in the benchmark in bpo-22458, which makes it quite a heavy part of the overall Fraction computation time.
The current implementation is
def gcd(a, b): while b: a, b = b, a%b return a
Reimplementing it in C would provide for much faster calculations. Here is a Cython version that simply drops the calculation loop into C as soon as the numbers are small enough to fit into a C long long int:
def _gcd(a, b): # Try doing all computation in C space. If the numbers are too large # at the beginning, retry until they are small enough. cdef long long ai, bi while b: try: ai, bi = a, b except OverflowError: pass else: # switch to C loop while bi: ai, bi = bi, ai%bi return ai a, b = b, a%b return a
It's substantially faster already because the values will either be small enough right from the start or quickly become so after a few iterations with Python objects.
Further improvements should be possible with a dedicated PyLong implementation based on Lehmer's GCD algorithm:
- addedperformancePerformance or resource usagePerformance or resource usagestdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directory
on Sep 24, 2014 In case it's useful, see issue bpo-1682 for my earlier Lehmer gcd implementation. At the time, that approach was dropped as being premature optimisation.
If Python grows an optimized implementation, how about exposing it in the math module?
If Python grows an optimized implementation, how about exposing it in the math module?
+1.
That's what the patch does anyway. +1
Hmm... which patch?
Here is updated Mark's patch from bpo-1682. It is ported to 3.5, slightly simplified and optimized (I did not touched the main algorithm still), utilized in the fractions module, added tests and documentation.
It speeds up Stefan's fractions benchmark about 20%.
- addedextension-modulesC modules in the Modules dirC modules in the Modules dirtype-featureA feature request or enhancementA feature request or enhancementand removedperformancePerformance or resource usagePerformance or resource usage
on Sep 25, 2014 The problem is that this changes the behaviour of fractions.gcd() w.r.t. negative numbers. It's a public function, so we should keep it for backwards compatibility reasons, *especially* when adding a new function in the math module.
29 remaining items
There are not such changes in patch 7. The fractions.gcd() function is unchanged but no longer used by the Fraction type, which now uses math.gcd() internally instead.
Ah, I misread; thanks.
What happens with this patch if a Fraction has been created with Integrals that aren't of type int? (E.g., with NumPy int32 instances, for example?)
Why patching fraction.Fraction constructor instead of fractions.gcd()?
I don't like the idea of having two functions, math.gcd and fractions.gcd, which do almost the same, but one is slow, whereas the other is fast. It's harder to write efficient code working on Python < 3.5 (use fractions) and Python >= 3.5 (use math or fractions?).
I suggest to modify fractions.gcd() to use math.gcd() if the two parameters are int. We just have to adjust the sign: if the second parameter is negative, return -math.gcd(a, b). (I guess that we have unit tests for fractions.gcd checking the 4 cases for signed parameters.)
I suggest to modify fractions.gcd() to use math.gcd() if the two parameters are int.
Sounds fine to me, so long as the code (both fractions.gcd and the fractions.Fraction implementation) continues to function as before for objects that don't have exact type int.
+1
I mean, there is already such a type check in Fraction.__init__(), but I can see a case for also optimising fraction.gcd() for exact ints.
One other suggestion: I think math.gcd should work with arbitrary Python objects implementing __index__, and not just with instances of int.
I mean, there is already such a type check in Fraction.__init__()
That type-check doesn't protect us from non-int Integrals, though, as far as I can tell. It looks to me as though doing
Fraction(numpy.int32(3), numpy.int32(2))would fail with a TypeError after this patch. (It works in Python 3.4.)I suggest just add deprecation warning in fractions.gcd(). Or at least add
notes which recommend math.gcd() in the docstring and the documentation of
fractions.gcd().One other suggestion: I think math.gcd should work with arbitrary Python
objects implementing __index__, and not just with instances of int.Agree.
What's the status of this issue? See also the issue bpo-22477.
Here is a patch which addresses both Mark's suggestions.
- math.gcd() now work with arbitrary Python objects implementing __index__.
- fractions.gcd() and Fraction's constructor now use math.gcd() if both arguments are int, but also support non-ints (e.g. Fractions or floats).
- fractions.gcd() now is deprecated.
But before committing I want to experiment with simpler implementation and compare it with current complex implementation. If the difference will be not too large, we could use simpler implementation.
Any more comments on this? The deadlines for new features in Py3.5 are getting closer. It seems we're just discussing details here, but pretty much everyone wants this feature.
So, what are the things that still need to be done? Serhiy submitted working patches months ago.
New changeset 34648ce02bd4 by Serhiy Storchaka in branch 'default':
Issue bpo-22486: Added the math.gcd() function. The fractions.gcd() function now is
https://hg.python.org/cpython/rev/34648ce02bd4
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields: