[Python-ideas] Yet another sum function (fractions.sum)
Peter Otten
__peter__ at web.de
Wed Aug 21 08:25:04 CEST 2013
Oscar Benjamin wrote:
> On 19 August 2013 11:09, Peter Otten
> <__peter__ at web.de> wrote:
>> sum(items, 0.0) would then automatically profit from the clever
>> optimizations of math.fsum() etc.
>
> fsum() is about controlling accumulated rounding errors rather than
> optimisation (although it may be faster I've never needed to check).
Is I understand it, you can "optimize" for precision, memory usage, code
simplicity -- not just speed.
>
> I'd rather write
> sum(items, Fraction)
> than
> sum(items, Fraction(0))
> and either way it's so close to
> Fraction.sum(items)
>
> I understand what you mean about having a single function that does a
> good job for all types and it goes into a much broader set of issues
> around how Python handles different numeric types. It would be
> possible in a backward compatible way provided Fraction.__sum__ falls
> back on sum when it finds a non-Rational.
>
> There are lots of other areas in the stdlib where a similar sort of
> thinking could apply e.g.:
>
>>>> import math
>>>> from decimal import Decimal as D
>>>> from fractions import Fraction as F
>
> There's currently no fsum equivalent for Decimals even though a pure
> Python implementation could have reasonable performance:
>
>>>> math.fsum([0.1, 0.2, 0.3]) == 0.6
> True
>>>> math.fsum([D('0.1'), D('0.2'), D('0.3')]) == D('0.6')
> False
>>>> D(math.fsum([D('0.1'), D('0.2'), D('0.3')])) == D('0.6')
> False
>
> sum() would do better in the above but then it fails in other situations:
>
>>>> sum([D('1e50'), D('1'), D('-1e50')]) == 1
> False
>>>> math.fsum([D('1e50'), D('1'), D('-1e50')]) == 1
> True
>
> The math module rounds everything to float losing precision even if
> better routines are available:
>
>>>> math.sqrt(D('0.02'))
> 0.1414213562373095
>>>> D('0.02').sqrt()
> Decimal('0.1414213562373095048801688724')
>>>> math.exp(D(1))
> 2.718281828459045
>>>> D(1).exp()
> Decimal('2.718281828459045235360287471')
>
> There's also no support for computing the other transcendental
> functions with Decimals e.g. sin, cos etc. without rounding to float.
>
> The math module also fails to find exact results when possible:
>
>>>> a = 10 ** 20 + 1
>>>> a
> 100000000000000000001
>>>> math.sqrt(a**2) == a
> False
>>>> b = F(2, 3)
>>>> math.sqrt(b ** 2) == b
> False
>
> These ones could just get fixed in the Fractions module:
>
>>>> F(4, 9) ** F(1, 2)
> 0.6666666666666666
>>>> F(2, 3) ** 2
> Fraction(4, 9)
>>>> a
> 100000000000000000001
>>>> (a ** 2) ** F(1, 2) == a
> False
>
> Do you think that any of these things should also be changed so that
> there can be e.g. one sqrt() function that does the right thing for
> all types?
At the time I only thought about sum(), but yes, for every operation that
has one "best" implementation per class there should be a uniform way to
make these implementations available.
> One way to unify these things is with a load of new dunder
> methods so that each type can implement its own response to the
> standard function. Another is to have special modules that deal with
> different things and e.g. a function for summing Rationals and another
> for summing inexact types and so on.
>
> Another possibility is that you have decimal functions in the decimal
> module and fraction functions in the fractions module and so on and
> don't use any duck-typing (except in coercion). That may seem strange
> for Python but it's worth remembering that most of the best numerical
> code that has ever been written was written in languages that
> *require* you to write separate code for each numeric type and don't
> give any syntactic support for working with non-native types.
Classmethods would be yet another option
float.sum(numbers)
or
complex.sqrt(a)
don't look bad, though I'm not sure what the right approach for int.sqrt()
would be...
More information about the Python-ideas
mailing list