Skip to content

Deterministic sum #1

Open
Open
@waldemarhorwat

Description

I propose making the sum be the deterministic mathematical sum of the inputs, rounded to the nearest double, breaking ties to even in the typical IEEE double fashion. The special cases are as for regular addition:

  • If any of the addends are NaN, the result is NaN.
  • Otherwise, if +∞ and -∞ both appear in the addends, the result is NaN.
  • Otherwise, if +∞ appears in the addends, the result is +∞.
  • Otherwise, if -∞ appears in the addends, the result is -∞.
  • Otherwise, if all of the addends are -0 (including the case where there are no addends), the result is -0.
  • Otherwise all the addends are finite. The result is the exact mathematical sum of the mathematical values of the addends, converted to a Number (note that the final conversion might overflow to ±∞).

This can be implemented as follows, using the exact intermediate sum technique which takes an arbitrary number of finite IEEE double addends a0, a1, …, an-1 and, barring intermediate or final overflows, represents their exact mathematical sum a0 + a1 + … + an-1 as s = p0 + p1 + …+ pk-1, where the p's are IEEE doubles, all have the same sign, are in descending magnitude order, are non-overlapping, meaning that each successive p's exponent is at least 53 less than the previous one, and are nonzero (except possibly p0).

  1. If there are no addends, the result is -0.
  2. If there is one addend a0, the result is a0.
  3. If there are two addends a0 and a1, the result is a0 + a1 using ordinary IEEE double arithmetic.
  4. If any of the addends are non-finite (±∞ or NaN), ignore all of the finite addends, and return the sum of the non-finite addends using ordinary IEEE double arithmetic done in any order.
  5. If all of the addends are -0, return -0 (depending on the implementation of exact intermediate sums, this case might fall out of the implementation of step 6 without needing to explicitly check for it.)
  6. Initialize an exact intermediate sum t = «pi» to t = «». Successively add addends into t using the exact intermediate sum technique above. To avoid false intermediate overflows, when picking the next addend to add into t, always prefer to pick an addend with the opposite sign from t if any exist. This way any intermediate overflow that happens is a true overflow and the mathematical sum will not be cancelled by later addends back into the finite IEEE double range.
  7. Round p0 + p1 + …+ pk-1 to an IEEE double by adding them using double arithmetic and return that result, taking special care to adjust the lsb around the round-to-even case in the case of a mathematical result exactly in between two representable doubles (see Python's math_fsum code for an explanation of this).

This is the simplest approach. The algorithm is asymptotically linear in the number of addends n and efficient, typically having k no more than 2.

The steps can be folded into a mostly or fully single-pass algorithm. For example:

  • Step 4 can be done by checking addends as they arrive and abandoning any finite addend sum the first time a non-finite addend is seen.
  • Step 6 scans for addends of the opposite sign from the running total. This can be done lazily by only doing this scan if an intermediate overflow actually happens.

If one wishes to do a fully single-pass algorithm, one can avoid the scan for addends of the opposite sign from the running total by scaling down the most significant partial p0 and values added into it by 52 powers of 2 if it would overflow, while leaving the other partials p1, …, pk-1 unscaled. This is a bit tedious requiring scaling when working across the first and second partial but can be done efficiently.

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions