<!-- canonical: https://efficientnewlanguage.org/eml-p/projects/P169-float-sums/ | updated: 2026-10-19 -->

# P169 Float sums

Four workers' partial sums added as they arrive: a loop and Kahan's sum give five different totals over the 24 orders, and Neumaier's sum the true one in all of them.

EML-P project `projects/float-sums` in the EML language repo: 2 module(s), entry `main.eml`, terminal UI. There, `eml project run projects/float-sums` runs it and `eml project verify projects/float-sums` replays every session under CPython (two hash seeds) and in the interpreter; the site build replays every session in the interpreter again and publishes a session only if its screen matches.

Built on verified corpus cases: the-order-the-workers-finished-changed-the-total (https://efficientnewlanguage.org/cases/469-the-order-the-workers-finished-changed-the-total/), the-compensated-sum-and-the-loop-disagree (https://efficientnewlanguage.org/cases/463-the-compensated-sum-and-the-loop-disagree/).

## Sessions

### bad-input - interpreter: byte-equal to the golden

Input:

```text
0
x
4

4
5
4
1 2 3 4 5 6 7
4
1 x
4
1.
4
.5 1
4
1e100 2
4
--1 2
4
1,5 2
4
123456789012345678 1
4
-2.5 1e-3 7E+2
5
```

Screen:

```text
== Float sums ==
Partial sums added as they arrive, and what the total depends on.

1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit
choice> 0
Pick a number from 1 to 5.

1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit
choice> x
Pick a number from 1 to 5.

1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit
choice> 4
2 to 6 numbers, separated by spaces> 
Cancelled.

1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit
choice> 4
2 to 6 numbers, separated by spaces> 5
Type 2 to 6 numbers such as 3, -2.5 or 1e16, separated by spaces.

1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit
choice> 4
2 to 6 numbers, separated by spaces> 1 2 3 4 5 6 7
Type 2 to 6 numbers such as 3, -2.5 or 1e16, separated by spaces.

1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit
choice> 4
2 to 6 numbers, separated by spaces> 1 x
Type 2 to 6 numbers such as 3, -2.5 or 1e16, separated by spaces.

1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit
choice> 4
2 to 6 numbers, separated by spaces> 1.
Type 2 to 6 numbers such as 3, -2.5 or 1e16, separated by spaces.

1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit
choice> 4
2 to 6 numbers, separated by spaces> .5 1
Type 2 to 6 numbers such as 3, -2.5 or 1e16, separated by spaces.

1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit
choice> 4
2 to 6 numbers, separated by spaces> 1e100 2
Type 2 to 6 numbers such as 3, -2.5 or 1e16, separated by spaces.

1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit
choice> 4
2 to 6 numbers, separated by spaces> --1 2
Type 2 to 6 numbers such as 3, -2.5 or 1e16, separated by spaces.

1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit
choice> 4
2 to 6 numbers, separated by spaces> 1,5 2
Type 2 to 6 numbers such as 3, -2.5 or 1e16, separated by spaces.

1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit
choice> 4
2 to 6 numbers, separated by spaces> 123456789012345678 1
Type 2 to 6 numbers such as 3, -2.5 or 1e16, separated by spaces.

1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit
choice> 4
2 to 6 numbers, separated by spaces> -2.5 1e-3 7E+2
In the 6 orders of these numbers: the loop always gives 697.501; Kahan's sum always gives 697.501; Neumaier's sum always gives 697.501.

1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit
choice> 5
Bye.
```

### basic - interpreter: byte-equal to the golden

Input:

```text
1
2
3
4
1e16 3 -1e16 2
4
0.1 0.2 0.3
4
1 2
5
```

Screen:

```text
== Float sums ==
Partial sums added as they arrive, and what the total depends on.

1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit
choice> 1
Four workers sum a day's account movements, and the reducer adds their partials as they arrive:
  inflows      1e+16
  outflows    -1e+16
  fees           1.0
  interest       0.5
The true total is 1.5: the inflows and the outflows cancel.

1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit
choice> 2
  arrival order                           loop   Kahan  Neumaier
  inflows, outflows, fees, interest        1.5     1.5       1.5
  inflows, outflows, interest, fees        1.5     1.5       1.5
  inflows, fees, outflows, interest        0.5     0.5       1.5
  inflows, fees, interest, outflows        0.0     2.0       1.5
  inflows, interest, outflows, fees        1.0     1.0       1.5
  inflows, interest, fees, outflows        0.0     2.0       1.5
  outflows, inflows, fees, interest        1.5     1.5       1.5
  outflows, inflows, interest, fees        1.5     1.5       1.5
  outflows, fees, inflows, interest        0.5     0.5       1.5
  outflows, fees, interest, inflows        0.0     2.0       1.5
  outflows, interest, inflows, fees        1.0     1.0       1.5
  outflows, interest, fees, inflows        0.0     2.0       1.5
  fees, inflows, outflows, interest        0.5     0.5       1.5
  fees, inflows, interest, outflows        0.0     0.0       1.5
  fees, outflows, inflows, interest        0.5     0.5       1.5
  fees, outflows, interest, inflows        0.0     0.0       1.5
  fees, interest, inflows, outflows        2.0     2.0       1.5
  fees, interest, outflows, inflows        2.0     2.0       1.5
  interest, inflows, outflows, fees        1.0     1.0       1.5
  interest, inflows, fees, outflows        0.0     0.0       1.5
  interest, outflows, inflows, fees        1.0     1.0       1.5
  interest, outflows, fees, inflows        0.0     0.0       1.5
  interest, fees, inflows, outflows        2.0     2.0       1.5
  interest, fees, outflows, inflows        2.0     2.0       1.5
The loop: 5 different totals, and 1.5 in 4 of the 24 orders. The answer is a function of which worker finished first.
Kahan's sum: 5 different totals, 1.5 in 4. Its correction is exact only while the running total is the larger number, and it is folded into the next number: next to 1e+16 the small partials are lost either way.
Neumaier's sum: 1.5 in 24 of the 24 orders. It keeps what each addition loses apart from the total, and adds it once, at the end.

1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit
choice> 3
Ten values of 0.1, added one by one:
  values  loop                  Kahan                 Neumaier
  1       0.1                   0.1                   0.1
  2       0.2                   0.2                   0.2
  3       0.30000000000000004   0.30000000000000004   0.30000000000000004
  4       0.4                   0.4                   0.4
  5       0.5                   0.5                   0.5
  6       0.6                   0.6000000000000001    0.6000000000000001
  7       0.7                   0.7000000000000001    0.7000000000000001
  8       0.7999999999999999    0.8                   0.8
  9       0.8999999999999999    0.9                   0.9
  10      0.9999999999999999    1.0                   1.0
The loop parts from the compensated sums at 6 values, and ten of them come to 0.9999999999999999, which is not 1.0. Kept in whole cents, 10 + 10 + ... is 100 exactly.

1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit
choice> 4
2 to 6 numbers, separated by spaces> 1e16 3 -1e16 2
In the 24 orders of these numbers: the loop gives 3 different totals, from 4.0 to 6.0; Kahan's sum gives 3 different totals, from 4.0 to 6.0; Neumaier's sum always gives 5.0.

1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit
choice> 4
2 to 6 numbers, separated by spaces> 0.1 0.2 0.3
In the 6 orders of these numbers: the loop gives 2 different totals, from 0.6 to 0.6000000000000001; Kahan's sum always gives 0.6; Neumaier's sum always gives 0.6.

1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit
choice> 4
2 to 6 numbers, separated by spaces> 1 2
In the 2 orders of these numbers: the loop always gives 3.0; Kahan's sum always gives 3.0; Neumaier's sum always gives 3.0.

1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit
choice> 5
Bye.
```

## Modules

### main.eml

```eml
# P169 float sums: four workers' partials added as they arrive - the
# partials, every arrival order through a loop, Kahan's sum and Neumaier's,
# ten values of 0.1, and numbers of your own.
import sums

def plain(s):
    0 => i
    len(s) => j
    while i < j and s[i] == " ":
        i + 1 => i
    while j > i and s[j - 1] == " ":
        j - 1 => j
    return s[i:j]

def pad(s, n):
    s => out
    while len(out) < n:
        out + " " => out
    return out

def lpad(s, n):
    s => out
    while len(out) < n:
        " " + out => out
    return out

def digits(t):
    for c in t:
        if c < "0" or c > "9":
            return False
    return len(t) > 0

def joined(names):
    "" => out
    for n in names:
        if out != "":
            out + ", " => out
        out + n => out
    return out

def show_partials():
    "Four workers sum a day's account movements, and the reducer adds their partials as they arrive:" ^0
    for p in sums.partials():
        ("  " + pad(p[0], 10) + lpad(str(p[1]), 8)) ^0
    "The true total is 1.5: the inflows and the outflows cancel." ^0

def tally(totals, want):
    # [different totals, how many equal want]
    0 => right
    for t in totals:
        if t == want:
            right + 1 => right
    return [len(sums.distinct(totals)), right]

def show_orders():
    sums.partials() => ps
    ("  " + pad("arrival order", 38) + lpad("loop", 6) + lpad("Kahan", 8) + lpad("Neumaier", 10)) ^0
    [] => a
    [] => b
    [] => c
    for o in sums.orders([0, 1, 2, 3]):
        [] => names
        [] => xs
        for i in o:
            names + [ps[i][0]] => names
            xs + [ps[i][1]] => xs
        sums.loop(xs) => x
        sums.kahan(xs) => y
        sums.neumaier(xs) => z
        a + [x] => a
        b + [y] => b
        c + [z] => c
        ("  " + pad(joined(names), 38) + lpad(str(x), 6) + lpad(str(y), 8) + lpad(str(z), 10)) ^0
    tally(a, 1.5) => ta
    tally(b, 1.5) => tb
    tally(c, 1.5) => tc
    ("The loop: " + str(ta[0]) + " different totals, and 1.5 in " + str(ta[1]) + " of the 24 orders. The answer is a function of which worker finished first.") ^0
    ("Kahan's sum: " + str(tb[0]) + " different totals, 1.5 in " + str(tb[1]) + ". Its correction is exact only while the running total is the larger number, and it is folded into the next number: next to 1e+16 the small partials are lost either way.") ^0
    ("Neumaier's sum: 1.5 in " + str(tc[1]) + " of the 24 orders. It keeps what each addition loses apart from the total, and adds it once, at the end.") ^0

def show_tenths():
    "Ten values of 0.1, added one by one:" ^0
    ("  " + pad("values", 8) + pad("loop", 22) + pad("Kahan", 22) + "Neumaier") ^0
    [] => xs
    0 => first
    0 => k
    while k < 10:
        xs + [0.1] => xs
        sums.loop(xs) => x
        sums.neumaier(xs) => z
        ("  " + pad(str(k + 1), 8) + pad(str(x), 22) + pad(str(sums.kahan(xs)), 22) + str(z)) ^0
        if first == 0 and x != z:
            k + 1 => first
        k + 1 => k
    ("The loop parts from the compensated sums at " + str(first) + " values, and ten of them come to " + str(sums.loop(xs)) + ", which is not 1.0. Kept in whole cents, 10 + 10 + ... is 100 exactly.") ^0

def number(t):
    # True when t is a number such as 2, -3.5, 1e16 or 2.5e-3
    0 => i
    if len(t) > 0 and t[0] == "-":
        1 => i
    i => a
    while i < len(t) and t[i] >= "0" and t[i] <= "9":
        i + 1 => i
    if i == a or i - a > 17:
        return False
    if i < len(t) and t[i] == ".":
        i + 1 => i
        i => a
        while i < len(t) and t[i] >= "0" and t[i] <= "9":
            i + 1 => i
        if i == a or i - a > 17:
            return False
    if i < len(t) and (t[i] == "e" or t[i] == "E"):
        i + 1 => i
        if i < len(t) and (t[i] == "-" or t[i] == "+"):
            i + 1 => i
        i => a
        while i < len(t) and t[i] >= "0" and t[i] <= "9":
            i + 1 => i
        if i == a or i - a > 2:
            return False
    return i == len(t)

def words(t):
    [] => out
    "" => cur
    for c in t + " ":
        if c == " ":
            if cur != "":
                out + [cur] => out
                "" => cur
        else:
            cur + c => cur
    return out

def describe(name, totals):
    sums.distinct(totals) => d
    if len(d) == 1:
        return name + " always gives " + str(d[0])
    d[0] => lo
    d[0] => hi
    for v in d:
        if v < lo:
            v => lo
        if v > hi:
            v => hi
    return name + " gives " + str(len(d)) + " different totals, from " + str(lo) + " to " + str(hi)

def own():
    plain(input("2 to 6 numbers, separated by spaces> ")) => t
    if t == "":
        "Cancelled." ^0
        return
    words(t) => ws
    True => ok
    if len(ws) < 2 or len(ws) > 6:
        False => ok
    for w in ws:
        if not number(w):
            False => ok
    if not ok:
        "Type 2 to 6 numbers such as 3, -2.5 or 1e16, separated by spaces." ^0
        return
    [] => xs
    for w in ws:
        xs + [float(w)] => xs
    [] => a
    [] => b
    [] => c
    0 => n
    for o in sums.orders(xs):
        a + [sums.loop(o)] => a
        b + [sums.kahan(o)] => b
        c + [sums.neumaier(o)] => c
        n + 1 => n
    ("In the " + str(n) + " orders of these numbers: " + describe("the loop", a) + "; " + describe("Kahan's sum", b) + "; " + describe("Neumaier's sum", c) + ".") ^0

"== Float sums ==" ^0
"Partial sums added as they arrive, and what the total depends on." ^0
True => running
while running:
    "" ^0
    "1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit" ^0
    plain(input("choice> ")) => choice
    if choice == "1":
        show_partials()
    elif choice == "2":
        show_orders()
    elif choice == "3":
        show_tenths()
    elif choice == "4":
        own()
    elif choice == "5":
        False => running
    else:
        "Pick a number from 1 to 5." ^0
"Bye." ^0
```

Python projection of main.eml:

```python
import sums

def plain(s):
    i = 0
    j = len(s)
    while i < j and s[i] == " ":
        i = i + 1
    while j > i and s[j - 1] == " ":
        j = j - 1
    return s[i:j]

def pad(s, n):
    out = s
    while len(out) < n:
        out = out + " "
    return out

def lpad(s, n):
    out = s
    while len(out) < n:
        out = " " + out
    return out

def digits(t):
    for c in t:
        if c < "0" or c > "9":
            return False
    return len(t) > 0

def joined(names):
    out = ""
    for n in names:
        if out != "":
            out = out + ", "
        out = out + n
    return out

def show_partials():
    print("Four workers sum a day's account movements, and the reducer adds their partials as they arrive:")
    for p in sums.partials():
        print("  " + pad(p[0], 10) + lpad(str(p[1]), 8))
    print("The true total is 1.5: the inflows and the outflows cancel.")

def tally(totals, want):
    right = 0
    for t in totals:
        if t == want:
            right = right + 1
    return [len(sums.distinct(totals)), right]

def show_orders():
    ps = sums.partials()
    print("  " + pad("arrival order", 38) + lpad("loop", 6) + lpad("Kahan", 8) + lpad("Neumaier", 10))
    a = []
    b = []
    c = []
    for o in sums.orders([0, 1, 2, 3]):
        names = []
        xs = []
        for i in o:
            names = names + [ps[i][0]]
            xs = xs + [ps[i][1]]
        x = sums.loop(xs)
        y = sums.kahan(xs)
        z = sums.neumaier(xs)
        a = a + [x]
        b = b + [y]
        c = c + [z]
        print("  " + pad(joined(names), 38) + lpad(str(x), 6) + lpad(str(y), 8) + lpad(str(z), 10))
    ta = tally(a, 1.5)
    tb = tally(b, 1.5)
    tc = tally(c, 1.5)
    print("The loop: " + str(ta[0]) + " different totals, and 1.5 in " + str(ta[1]) + " of the 24 orders. The answer is a function of which worker finished first.")
    print("Kahan's sum: " + str(tb[0]) + " different totals, 1.5 in " + str(tb[1]) + ". Its correction is exact only while the running total is the larger number, and it is folded into the next number: next to 1e+16 the small partials are lost either way.")
    print("Neumaier's sum: 1.5 in " + str(tc[1]) + " of the 24 orders. It keeps what each addition loses apart from the total, and adds it once, at the end.")

def show_tenths():
    print("Ten values of 0.1, added one by one:")
    print("  " + pad("values", 8) + pad("loop", 22) + pad("Kahan", 22) + "Neumaier")
    xs = []
    first = 0
    k = 0
    while k < 10:
        xs = xs + [0.1]
        x = sums.loop(xs)
        z = sums.neumaier(xs)
        print("  " + pad(str(k + 1), 8) + pad(str(x), 22) + pad(str(sums.kahan(xs)), 22) + str(z))
        if first == 0 and x != z:
            first = k + 1
        k = k + 1
    print("The loop parts from the compensated sums at " + str(first) + " values, and ten of them come to " + str(sums.loop(xs)) + ", which is not 1.0. Kept in whole cents, 10 + 10 + ... is 100 exactly.")

def number(t):
    i = 0
    if len(t) > 0 and t[0] == "-":
        i = 1
    a = i
    while i < len(t) and t[i] >= "0" and t[i] <= "9":
        i = i + 1
    if i == a or i - a > 17:
        return False
    if i < len(t) and t[i] == ".":
        i = i + 1
        a = i
        while i < len(t) and t[i] >= "0" and t[i] <= "9":
            i = i + 1
        if i == a or i - a > 17:
            return False
    if i < len(t) and (t[i] == "e" or t[i] == "E"):
        i = i + 1
        if i < len(t) and (t[i] == "-" or t[i] == "+"):
            i = i + 1
        a = i
        while i < len(t) and t[i] >= "0" and t[i] <= "9":
            i = i + 1
        if i == a or i - a > 2:
            return False
    return i == len(t)

def words(t):
    out = []
    cur = ""
    for c in t + " ":
        if c == " ":
            if cur != "":
                out = out + [cur]
                cur = ""
        else:
            cur = cur + c
    return out

def describe(name, totals):
    d = sums.distinct(totals)
    if len(d) == 1:
        return name + " always gives " + str(d[0])
    lo = d[0]
    hi = d[0]
    for v in d:
        if v < lo:
            lo = v
        if v > hi:
            hi = v
    return name + " gives " + str(len(d)) + " different totals, from " + str(lo) + " to " + str(hi)

def own():
    t = plain(input("2 to 6 numbers, separated by spaces> "))
    if t == "":
        print("Cancelled.")
        return
    ws = words(t)
    ok = True
    if len(ws) < 2 or len(ws) > 6:
        ok = False
    for w in ws:
        if not number(w):
            ok = False
    if not ok:
        print("Type 2 to 6 numbers such as 3, -2.5 or 1e16, separated by spaces.")
        return
    xs = []
    for w in ws:
        xs = xs + [float(w)]
    a = []
    b = []
    c = []
    n = 0
    for o in sums.orders(xs):
        a = a + [sums.loop(o)]
        b = b + [sums.kahan(o)]
        c = c + [sums.neumaier(o)]
        n = n + 1
    print("In the " + str(n) + " orders of these numbers: " + describe("the loop", a) + "; " + describe("Kahan's sum", b) + "; " + describe("Neumaier's sum", c) + ".")

print("== Float sums ==")
print("Partial sums added as they arrive, and what the total depends on.")
running = True
while running:
    print("")
    print("1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit")
    choice = plain(input("choice> "))
    if choice == "1":
        show_partials()
    elif choice == "2":
        show_orders()
    elif choice == "3":
        show_tenths()
    elif choice == "4":
        own()
    elif choice == "5":
        running = False
    else:
        print("Pick a number from 1 to 5.")
print("Bye.")
```

### sums.eml

```eml
# P169 float sums - a reducer that adds the partial sums of four workers as
# they arrive. As in the corpus case
# the-order-the-workers-finished-changed-the-total, floating-point addition
# is not associative: where the partials differ greatly in size, a small one
# added to a large one is lost, and which are lost depends on the order. And
# as in the-compensated-sum-and-the-loop-disagree, a compensated sum keeps
# the part each step would lose: Kahan's while the running total is the
# larger number, Neumaier's whichever it is.

def partials():
    # [what the worker summed, its partial]
    return [["inflows", 10000000000000000.0], ["outflows", -10000000000000000.0], ["fees", 1.0], ["interest", 0.5]]

def absf(x):
    if x < 0:
        return 0 - x
    return x

def loop(xs):
    0.0 => t
    for x in xs:
        t + x => t
    return t

def kahan(xs):
    0.0 => s
    0.0 => c
    for x in xs:
        x - c => y
        s + y => t
        (t - s) - y => c
        t => s
    return s

def neumaier(xs):
    0.0 => s
    0.0 => c
    for x in xs:
        s + x => t
        if absf(s) >= absf(x):
            c + ((s - t) + x) => c
        else:
            c + ((x - t) + s) => c
        t => s
    return s + c

def orders(items):
    # every order of items, in the order itertools would list them
    if len(items) <= 1:
        return [items]
    [] => out
    0 => i
    while i < len(items):
        items[0:i] + items[i + 1:] => rest
        for p in orders(rest):
            out + [[items[i]] + p] => out
        i + 1 => i
    return out

def distinct(values):
    # the values in the order they first appear, each once
    [] => out
    for v in values:
        False => found
        for u in out:
            if u == v:
                True => found
        if not found:
            out + [v] => out
    return out
```

Python projection of sums.eml:

```python
def partials():
    return [["inflows", 10000000000000000.0], ["outflows", -10000000000000000.0], ["fees", 1.0], ["interest", 0.5]]

def absf(x):
    if x < 0:
        return 0 - x
    return x

def loop(xs):
    t = 0.0
    for x in xs:
        t = t + x
    return t

def kahan(xs):
    s = 0.0
    c = 0.0
    for x in xs:
        y = x - c
        t = s + y
        c = t - s - y
        s = t
    return s

def neumaier(xs):
    s = 0.0
    c = 0.0
    for x in xs:
        t = s + x
        if absf(s) >= absf(x):
            c = c + (s - t + x)
        else:
            c = c + (x - t + s)
        s = t
    return s + c

def orders(items):
    if len(items) <= 1:
        return [items]
    out = []
    i = 0
    while i < len(items):
        rest = items[0:i] + items[i + 1:]
        for p in orders(rest):
            out = out + [[items[i]] + p]
        i = i + 1
    return out

def distinct(values):
    out = []
    for v in values:
        found = False
        for u in out:
            if u == v:
                found = True
        if not found:
            out = out + [v]
    return out
```

## README

# P169 - Float sums

A reducer that adds four workers' partial sums as they arrive, and what
the total depends on.

- `main.eml` - the menu, the partials, every arrival order, ten values of
  0.1, and numbers of your own
- `sums.eml` - the partials; a plain loop, Kahan's compensated sum and
  Neumaier's; every order of a list; distinct values

How each part works:

- The partials are 1e+16 of inflows, -1e+16 of outflows, 1.0 of fees and
  0.5 of interest; the true total is 1.5.
- As in the corpus case `the-order-the-workers-finished-changed-the-total`,
  floating-point addition is not associative: next to 1e+16 a 1.0 or a 0.5
  is lost, and which are lost depends on the order. Over the 24 arrival
  orders the loop gives 5 different totals, 0.0 to 2.0, and 1.5 in 4.
- As in `the-compensated-sum-and-the-loop-disagree`, a compensated sum keeps
  what each addition loses. Kahan's correction is exact only while the
  running total is the larger number, and is folded into the next number,
  so here it does no better than the loop: 5 totals, 1.5 in 4. Neumaier's
  keeps what is lost apart and adds it at the end: 1.5 in all 24. Ten
  values of 0.1 come to 0.9999999999999999 in the loop and 1.0 in both
  compensated sums; the loop parts from them at six values.

What is checked: menu choices 1 to 5; 2 to 6 numbers such as 3, -2.5, 1e16
or 7E+2, separated by spaces, with at most 17 digits on either side of the
point and 2 in the exponent. An empty answer cancels.

Sessions: `sessions/basic.in` gives:
- the partials, every arrival order and the ten values of 0.1;
- numbers of your own: 1e16 3 -1e16 2, then 0.1 0.2 0.3, then 1 2.

`sessions/bad-input.in` gives:
- menu choices 0 and x;
- numbers: a cancel, one number, seven numbers, a letter, 1., .5, an
  exponent of 100, --1, 1,5 and eighteen digits;
- -2.5 1e-3 7E+2.

Built on the verified corpus cases `the-order-the-workers-finished-changed-the-total`
(three partial sums added in arrival order, every order enumerated) and
`the-compensated-sum-and-the-loop-disagree` (ten values of 0.1 added by a
loop and by a compensated sum).
