This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author tim.peters
Recipients mark.dickinson, rhettinger, serhiy.storchaka, tim.peters
Date 2018-08-11.21:56:50
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1534024610.7.0.56676864532.issue34376@psf.upfronthosting.co.za>
In-reply-to
Content
Thanks for doing the "real ulp" calc, Raymond!  It was intended to make the Kahan gimmick look better, and it succeeded ;-)  I don't personally care whether adding 10K things ends up with 50 ulp error, but to each their own.

Division can be mostly removed from scaling, but it's a bit delicate.  The obvious stab would be to multiply by 1/max instead of dividing by max.  That introduces another rounding error, but as you've already discovered this computation as a whole is relatively insensitive to rounding errors in scaling.  The real problem is that 1/max can overflow (when max is subnormal - the IEEE range isn't symmetric).

Instead a power of 2 could be used, chosen so that it and its reciprocal are both representable.  There's no particular virtue in scaling the largest value to 1.0 - the real point is to avoid spurious overflow/underflow when squaring the scaled x.  Code like the following could work.  Scaling would introduce essentially no rounding errors then.

But I bet a call to `ldexp()` is even more expensive than division on most platforms.  So it may well be slower when there are few points.

def hyp_ps(xs):
    b = max(map(abs, xs))
    _, exp = frexp(b)
    exp = -3 * exp // 4
    # scale and invscale are exact.
    # Multiplying by scale is exact, except when
    # the result becomes subnormal (in which case
    # |x| is insignifcant compared to |b|).
    # invscale isn't needed until the loop ends,
    # so the division time should overlap with the
    # loop body.
    scale = ldexp(1.0, exp) # exact
    invscale = 1.0 / scale  # exact
    # and `x * scale` is exact except for underflow
    s = fsum((x * scale)**2 for x in xs)
    return invscale * sqrt(s)
History
Date User Action Args
2018-08-11 21:56:50tim.peterssetrecipients: + tim.peters, rhettinger, mark.dickinson, serhiy.storchaka
2018-08-11 21:56:50tim.peterssetmessageid: <1534024610.7.0.56676864532.issue34376@psf.upfronthosting.co.za>
2018-08-11 21:56:50tim.peterslinkissue34376 messages
2018-08-11 21:56:50tim.peterscreate