[issue41458] Avoid overflow/underflow in math.prod()
Tim Peters
report at bugs.python.org
Sat Aug 8 13:30:17 EDT 2020
Tim Peters <tim at python.org> added the comment:
Well, that can't work: the most likely result for a long input is 0.0 (try it!). frexp() forces the mantissa into range [0.5, 1.0). Multiply N of those, and the result _can_ be as small as 2**-N. So, as in Mark's code, every thousand times (2**-1000 is nearing the subnormal boundary) frexp() is used again to force the product-so-far back into range. That's straightforward when going "left to right".
With fancier reduction schemes, "it varies". Aiming for "obviously correct" rather than for maximal cleverness ;-) , here I'll associate each partial product with an integer e such that it's guaranteed (in the absence of infinities, NaNs, zeroes), abs(partial_product) >= 2^^-e. Then quick integer arithmetic can be used in advance to guard against a partial product underflowing:
def frpair(seq):
from math import frexp, ldexp
stack = []
exp = 0
for i, x in enumerate(seq, start=1):
m, e = frexp(x)
exp += e
stack += [(m, 1)]
while not i&1:
i >>= 1
(x, e1), (y, e2) = stack[-2:]
esum = e1 + e2
if esum >= 1000:
x, e = frexp(x)
exp += e
y, e = frexp(y)
exp += e
esum = 2
stack[-2:] = [(x * y, esum)]
total = 1.0
totale = 0
while stack:
x, e = stack.pop()
totale += e
if totale >= 1000:
total, e = frexp(total)
exp += e
x, e = frexp(x)
exp += e
totale = 2
total *= x
return ldexp(total, exp)
But I see no obvious improvement in accuracy over "left to right" for the uniformly random test cases I've tried.
----------
_______________________________________
Python tracker <report at bugs.python.org>
<https://bugs.python.org/issue41458>
_______________________________________
More information about the Python-bugs-list
mailing list