{
  "id": "P169",
  "slug": "float-sums",
  "key": "P169-float-sums",
  "title": "Float sums",
  "summary": "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.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P169 - Float sums\n\nA reducer that adds four workers' partial sums as they arrive, and what\nthe total depends on.\n\n- `main.eml` - the menu, the partials, every arrival order, ten values of\n  0.1, and numbers of your own\n- `sums.eml` - the partials; a plain loop, Kahan's compensated sum and\n  Neumaier's; every order of a list; distinct values\n\nHow each part works:\n\n- The partials are 1e+16 of inflows, -1e+16 of outflows, 1.0 of fees and\n  0.5 of interest; the true total is 1.5.\n- As in the corpus case `the-order-the-workers-finished-changed-the-total`,\n  floating-point addition is not associative: next to 1e+16 a 1.0 or a 0.5\n  is lost, and which are lost depends on the order. Over the 24 arrival\n  orders the loop gives 5 different totals, 0.0 to 2.0, and 1.5 in 4.\n- As in `the-compensated-sum-and-the-loop-disagree`, a compensated sum keeps\n  what each addition loses. Kahan's correction is exact only while the\n  running total is the larger number, and is folded into the next number,\n  so here it does no better than the loop: 5 totals, 1.5 in 4. Neumaier's\n  keeps what is lost apart and adds it at the end: 1.5 in all 24. Ten\n  values of 0.1 come to 0.9999999999999999 in the loop and 1.0 in both\n  compensated sums; the loop parts from them at six values.\n\nWhat is checked: menu choices 1 to 5; 2 to 6 numbers such as 3, -2.5, 1e16\nor 7E+2, separated by spaces, with at most 17 digits on either side of the\npoint and 2 in the exponent. An empty answer cancels.\n\nSessions: `sessions/basic.in` gives:\n- the partials, every arrival order and the ten values of 0.1;\n- numbers of your own: 1e16 3 -1e16 2, then 0.1 0.2 0.3, then 1 2.\n\n`sessions/bad-input.in` gives:\n- menu choices 0 and x;\n- numbers: a cancel, one number, seven numbers, a letter, 1., .5, an\n  exponent of 100, --1, 1,5 and eighteen digits;\n- -2.5 1e-3 7E+2.\n\nBuilt on the verified corpus cases `the-order-the-workers-finished-changed-the-total`\n(three partial sums added in arrival order, every order enumerated) and\n`the-compensated-sum-and-the-loop-disagree` (ten values of 0.1 added by a\nloop and by a compensated sum).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P169 float sums: four workers' partials added as they arrive - the\n# partials, every arrival order through a loop, Kahan's sum and Neumaier's,\n# ten values of 0.1, and numbers of your own.\nimport sums\n\ndef plain(s):\n    0 => i\n    len(s) => j\n    while i < j and s[i] == \" \":\n        i + 1 => i\n    while j > i and s[j - 1] == \" \":\n        j - 1 => j\n    return s[i:j]\n\ndef pad(s, n):\n    s => out\n    while len(out) < n:\n        out + \" \" => out\n    return out\n\ndef lpad(s, n):\n    s => out\n    while len(out) < n:\n        \" \" + out => out\n    return out\n\ndef digits(t):\n    for c in t:\n        if c < \"0\" or c > \"9\":\n            return False\n    return len(t) > 0\n\ndef joined(names):\n    \"\" => out\n    for n in names:\n        if out != \"\":\n            out + \", \" => out\n        out + n => out\n    return out\n\ndef show_partials():\n    \"Four workers sum a day's account movements, and the reducer adds their partials as they arrive:\" ^0\n    for p in sums.partials():\n        (\"  \" + pad(p[0], 10) + lpad(str(p[1]), 8)) ^0\n    \"The true total is 1.5: the inflows and the outflows cancel.\" ^0\n\ndef tally(totals, want):\n    # [different totals, how many equal want]\n    0 => right\n    for t in totals:\n        if t == want:\n            right + 1 => right\n    return [len(sums.distinct(totals)), right]\n\ndef show_orders():\n    sums.partials() => ps\n    (\"  \" + pad(\"arrival order\", 38) + lpad(\"loop\", 6) + lpad(\"Kahan\", 8) + lpad(\"Neumaier\", 10)) ^0\n    [] => a\n    [] => b\n    [] => c\n    for o in sums.orders([0, 1, 2, 3]):\n        [] => names\n        [] => xs\n        for i in o:\n            names + [ps[i][0]] => names\n            xs + [ps[i][1]] => xs\n        sums.loop(xs) => x\n        sums.kahan(xs) => y\n        sums.neumaier(xs) => z\n        a + [x] => a\n        b + [y] => b\n        c + [z] => c\n        (\"  \" + pad(joined(names), 38) + lpad(str(x), 6) + lpad(str(y), 8) + lpad(str(z), 10)) ^0\n    tally(a, 1.5) => ta\n    tally(b, 1.5) => tb\n    tally(c, 1.5) => tc\n    (\"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\n    (\"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\n    (\"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\n\ndef show_tenths():\n    \"Ten values of 0.1, added one by one:\" ^0\n    (\"  \" + pad(\"values\", 8) + pad(\"loop\", 22) + pad(\"Kahan\", 22) + \"Neumaier\") ^0\n    [] => xs\n    0 => first\n    0 => k\n    while k < 10:\n        xs + [0.1] => xs\n        sums.loop(xs) => x\n        sums.neumaier(xs) => z\n        (\"  \" + pad(str(k + 1), 8) + pad(str(x), 22) + pad(str(sums.kahan(xs)), 22) + str(z)) ^0\n        if first == 0 and x != z:\n            k + 1 => first\n        k + 1 => k\n    (\"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\n\ndef number(t):\n    # True when t is a number such as 2, -3.5, 1e16 or 2.5e-3\n    0 => i\n    if len(t) > 0 and t[0] == \"-\":\n        1 => i\n    i => a\n    while i < len(t) and t[i] >= \"0\" and t[i] <= \"9\":\n        i + 1 => i\n    if i == a or i - a > 17:\n        return False\n    if i < len(t) and t[i] == \".\":\n        i + 1 => i\n        i => a\n        while i < len(t) and t[i] >= \"0\" and t[i] <= \"9\":\n            i + 1 => i\n        if i == a or i - a > 17:\n            return False\n    if i < len(t) and (t[i] == \"e\" or t[i] == \"E\"):\n        i + 1 => i\n        if i < len(t) and (t[i] == \"-\" or t[i] == \"+\"):\n            i + 1 => i\n        i => a\n        while i < len(t) and t[i] >= \"0\" and t[i] <= \"9\":\n            i + 1 => i\n        if i == a or i - a > 2:\n            return False\n    return i == len(t)\n\ndef words(t):\n    [] => out\n    \"\" => cur\n    for c in t + \" \":\n        if c == \" \":\n            if cur != \"\":\n                out + [cur] => out\n                \"\" => cur\n        else:\n            cur + c => cur\n    return out\n\ndef describe(name, totals):\n    sums.distinct(totals) => d\n    if len(d) == 1:\n        return name + \" always gives \" + str(d[0])\n    d[0] => lo\n    d[0] => hi\n    for v in d:\n        if v < lo:\n            v => lo\n        if v > hi:\n            v => hi\n    return name + \" gives \" + str(len(d)) + \" different totals, from \" + str(lo) + \" to \" + str(hi)\n\ndef own():\n    plain(input(\"2 to 6 numbers, separated by spaces> \")) => t\n    if t == \"\":\n        \"Cancelled.\" ^0\n        return\n    words(t) => ws\n    True => ok\n    if len(ws) < 2 or len(ws) > 6:\n        False => ok\n    for w in ws:\n        if not number(w):\n            False => ok\n    if not ok:\n        \"Type 2 to 6 numbers such as 3, -2.5 or 1e16, separated by spaces.\" ^0\n        return\n    [] => xs\n    for w in ws:\n        xs + [float(w)] => xs\n    [] => a\n    [] => b\n    [] => c\n    0 => n\n    for o in sums.orders(xs):\n        a + [sums.loop(o)] => a\n        b + [sums.kahan(o)] => b\n        c + [sums.neumaier(o)] => c\n        n + 1 => n\n    (\"In the \" + str(n) + \" orders of these numbers: \" + describe(\"the loop\", a) + \"; \" + describe(\"Kahan's sum\", b) + \"; \" + describe(\"Neumaier's sum\", c) + \".\") ^0\n\n\"== Float sums ==\" ^0\n\"Partial sums added as they arrive, and what the total depends on.\" ^0\nTrue => running\nwhile running:\n    \"\" ^0\n    \"1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit\" ^0\n    plain(input(\"choice> \")) => choice\n    if choice == \"1\":\n        show_partials()\n    elif choice == \"2\":\n        show_orders()\n    elif choice == \"3\":\n        show_tenths()\n    elif choice == \"4\":\n        own()\n    elif choice == \"5\":\n        False => running\n    else:\n        \"Pick a number from 1 to 5.\" ^0\n\"Bye.\" ^0\n",
      "python": "import sums\n\ndef plain(s):\n    i = 0\n    j = len(s)\n    while i < j and s[i] == \" \":\n        i = i + 1\n    while j > i and s[j - 1] == \" \":\n        j = j - 1\n    return s[i:j]\n\ndef pad(s, n):\n    out = s\n    while len(out) < n:\n        out = out + \" \"\n    return out\n\ndef lpad(s, n):\n    out = s\n    while len(out) < n:\n        out = \" \" + out\n    return out\n\ndef digits(t):\n    for c in t:\n        if c < \"0\" or c > \"9\":\n            return False\n    return len(t) > 0\n\ndef joined(names):\n    out = \"\"\n    for n in names:\n        if out != \"\":\n            out = out + \", \"\n        out = out + n\n    return out\n\ndef show_partials():\n    print(\"Four workers sum a day's account movements, and the reducer adds their partials as they arrive:\")\n    for p in sums.partials():\n        print(\"  \" + pad(p[0], 10) + lpad(str(p[1]), 8))\n    print(\"The true total is 1.5: the inflows and the outflows cancel.\")\n\ndef tally(totals, want):\n    right = 0\n    for t in totals:\n        if t == want:\n            right = right + 1\n    return [len(sums.distinct(totals)), right]\n\ndef show_orders():\n    ps = sums.partials()\n    print(\"  \" + pad(\"arrival order\", 38) + lpad(\"loop\", 6) + lpad(\"Kahan\", 8) + lpad(\"Neumaier\", 10))\n    a = []\n    b = []\n    c = []\n    for o in sums.orders([0, 1, 2, 3]):\n        names = []\n        xs = []\n        for i in o:\n            names = names + [ps[i][0]]\n            xs = xs + [ps[i][1]]\n        x = sums.loop(xs)\n        y = sums.kahan(xs)\n        z = sums.neumaier(xs)\n        a = a + [x]\n        b = b + [y]\n        c = c + [z]\n        print(\"  \" + pad(joined(names), 38) + lpad(str(x), 6) + lpad(str(y), 8) + lpad(str(z), 10))\n    ta = tally(a, 1.5)\n    tb = tally(b, 1.5)\n    tc = tally(c, 1.5)\n    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.\")\n    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.\")\n    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.\")\n\ndef show_tenths():\n    print(\"Ten values of 0.1, added one by one:\")\n    print(\"  \" + pad(\"values\", 8) + pad(\"loop\", 22) + pad(\"Kahan\", 22) + \"Neumaier\")\n    xs = []\n    first = 0\n    k = 0\n    while k < 10:\n        xs = xs + [0.1]\n        x = sums.loop(xs)\n        z = sums.neumaier(xs)\n        print(\"  \" + pad(str(k + 1), 8) + pad(str(x), 22) + pad(str(sums.kahan(xs)), 22) + str(z))\n        if first == 0 and x != z:\n            first = k + 1\n        k = k + 1\n    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.\")\n\ndef number(t):\n    i = 0\n    if len(t) > 0 and t[0] == \"-\":\n        i = 1\n    a = i\n    while i < len(t) and t[i] >= \"0\" and t[i] <= \"9\":\n        i = i + 1\n    if i == a or i - a > 17:\n        return False\n    if i < len(t) and t[i] == \".\":\n        i = i + 1\n        a = i\n        while i < len(t) and t[i] >= \"0\" and t[i] <= \"9\":\n            i = i + 1\n        if i == a or i - a > 17:\n            return False\n    if i < len(t) and (t[i] == \"e\" or t[i] == \"E\"):\n        i = i + 1\n        if i < len(t) and (t[i] == \"-\" or t[i] == \"+\"):\n            i = i + 1\n        a = i\n        while i < len(t) and t[i] >= \"0\" and t[i] <= \"9\":\n            i = i + 1\n        if i == a or i - a > 2:\n            return False\n    return i == len(t)\n\ndef words(t):\n    out = []\n    cur = \"\"\n    for c in t + \" \":\n        if c == \" \":\n            if cur != \"\":\n                out = out + [cur]\n                cur = \"\"\n        else:\n            cur = cur + c\n    return out\n\ndef describe(name, totals):\n    d = sums.distinct(totals)\n    if len(d) == 1:\n        return name + \" always gives \" + str(d[0])\n    lo = d[0]\n    hi = d[0]\n    for v in d:\n        if v < lo:\n            lo = v\n        if v > hi:\n            hi = v\n    return name + \" gives \" + str(len(d)) + \" different totals, from \" + str(lo) + \" to \" + str(hi)\n\ndef own():\n    t = plain(input(\"2 to 6 numbers, separated by spaces> \"))\n    if t == \"\":\n        print(\"Cancelled.\")\n        return\n    ws = words(t)\n    ok = True\n    if len(ws) < 2 or len(ws) > 6:\n        ok = False\n    for w in ws:\n        if not number(w):\n            ok = False\n    if not ok:\n        print(\"Type 2 to 6 numbers such as 3, -2.5 or 1e16, separated by spaces.\")\n        return\n    xs = []\n    for w in ws:\n        xs = xs + [float(w)]\n    a = []\n    b = []\n    c = []\n    n = 0\n    for o in sums.orders(xs):\n        a = a + [sums.loop(o)]\n        b = b + [sums.kahan(o)]\n        c = c + [sums.neumaier(o)]\n        n = n + 1\n    print(\"In the \" + str(n) + \" orders of these numbers: \" + describe(\"the loop\", a) + \"; \" + describe(\"Kahan's sum\", b) + \"; \" + describe(\"Neumaier's sum\", c) + \".\")\n\nprint(\"== Float sums ==\")\nprint(\"Partial sums added as they arrive, and what the total depends on.\")\nrunning = True\nwhile running:\n    print(\"\")\n    print(\"1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit\")\n    choice = plain(input(\"choice> \"))\n    if choice == \"1\":\n        show_partials()\n    elif choice == \"2\":\n        show_orders()\n    elif choice == \"3\":\n        show_tenths()\n    elif choice == \"4\":\n        own()\n    elif choice == \"5\":\n        running = False\n    else:\n        print(\"Pick a number from 1 to 5.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "sums.eml",
      "eml": "# P169 float sums - a reducer that adds the partial sums of four workers as\n# they arrive. As in the corpus case\n# the-order-the-workers-finished-changed-the-total, floating-point addition\n# is not associative: where the partials differ greatly in size, a small one\n# added to a large one is lost, and which are lost depends on the order. And\n# as in the-compensated-sum-and-the-loop-disagree, a compensated sum keeps\n# the part each step would lose: Kahan's while the running total is the\n# larger number, Neumaier's whichever it is.\n\ndef partials():\n    # [what the worker summed, its partial]\n    return [[\"inflows\", 10000000000000000.0], [\"outflows\", -10000000000000000.0], [\"fees\", 1.0], [\"interest\", 0.5]]\n\ndef absf(x):\n    if x < 0:\n        return 0 - x\n    return x\n\ndef loop(xs):\n    0.0 => t\n    for x in xs:\n        t + x => t\n    return t\n\ndef kahan(xs):\n    0.0 => s\n    0.0 => c\n    for x in xs:\n        x - c => y\n        s + y => t\n        (t - s) - y => c\n        t => s\n    return s\n\ndef neumaier(xs):\n    0.0 => s\n    0.0 => c\n    for x in xs:\n        s + x => t\n        if absf(s) >= absf(x):\n            c + ((s - t) + x) => c\n        else:\n            c + ((x - t) + s) => c\n        t => s\n    return s + c\n\ndef orders(items):\n    # every order of items, in the order itertools would list them\n    if len(items) <= 1:\n        return [items]\n    [] => out\n    0 => i\n    while i < len(items):\n        items[0:i] + items[i + 1:] => rest\n        for p in orders(rest):\n            out + [[items[i]] + p] => out\n        i + 1 => i\n    return out\n\ndef distinct(values):\n    # the values in the order they first appear, each once\n    [] => out\n    for v in values:\n        False => found\n        for u in out:\n            if u == v:\n                True => found\n        if not found:\n            out + [v] => out\n    return out\n",
      "python": "def partials():\n    return [[\"inflows\", 10000000000000000.0], [\"outflows\", -10000000000000000.0], [\"fees\", 1.0], [\"interest\", 0.5]]\n\ndef absf(x):\n    if x < 0:\n        return 0 - x\n    return x\n\ndef loop(xs):\n    t = 0.0\n    for x in xs:\n        t = t + x\n    return t\n\ndef kahan(xs):\n    s = 0.0\n    c = 0.0\n    for x in xs:\n        y = x - c\n        t = s + y\n        c = t - s - y\n        s = t\n    return s\n\ndef neumaier(xs):\n    s = 0.0\n    c = 0.0\n    for x in xs:\n        t = s + x\n        if absf(s) >= absf(x):\n            c = c + (s - t + x)\n        else:\n            c = c + (x - t + s)\n        s = t\n    return s + c\n\ndef orders(items):\n    if len(items) <= 1:\n        return [items]\n    out = []\n    i = 0\n    while i < len(items):\n        rest = items[0:i] + items[i + 1:]\n        for p in orders(rest):\n            out = out + [[items[i]] + p]\n        i = i + 1\n    return out\n\ndef distinct(values):\n    out = []\n    for v in values:\n        found = False\n        for u in out:\n            if u == v:\n                found = True\n        if not found:\n            out = out + [v]\n    return out\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "0\nx\n4\n\n4\n5\n4\n1 2 3 4 5 6 7\n4\n1 x\n4\n1.\n4\n.5 1\n4\n1e100 2\n4\n--1 2\n4\n1,5 2\n4\n123456789012345678 1\n4\n-2.5 1e-3 7E+2\n5\n",
      "screen": "== Float sums ==\nPartial sums added as they arrive, and what the total depends on.\n\n1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit\nchoice> 0\nPick a number from 1 to 5.\n\n1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit\nchoice> x\nPick a number from 1 to 5.\n\n1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit\nchoice> 4\n2 to 6 numbers, separated by spaces> \nCancelled.\n\n1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit\nchoice> 4\n2 to 6 numbers, separated by spaces> 5\nType 2 to 6 numbers such as 3, -2.5 or 1e16, separated by spaces.\n\n1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit\nchoice> 4\n2 to 6 numbers, separated by spaces> 1 2 3 4 5 6 7\nType 2 to 6 numbers such as 3, -2.5 or 1e16, separated by spaces.\n\n1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit\nchoice> 4\n2 to 6 numbers, separated by spaces> 1 x\nType 2 to 6 numbers such as 3, -2.5 or 1e16, separated by spaces.\n\n1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit\nchoice> 4\n2 to 6 numbers, separated by spaces> 1.\nType 2 to 6 numbers such as 3, -2.5 or 1e16, separated by spaces.\n\n1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit\nchoice> 4\n2 to 6 numbers, separated by spaces> .5 1\nType 2 to 6 numbers such as 3, -2.5 or 1e16, separated by spaces.\n\n1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit\nchoice> 4\n2 to 6 numbers, separated by spaces> 1e100 2\nType 2 to 6 numbers such as 3, -2.5 or 1e16, separated by spaces.\n\n1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit\nchoice> 4\n2 to 6 numbers, separated by spaces> --1 2\nType 2 to 6 numbers such as 3, -2.5 or 1e16, separated by spaces.\n\n1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit\nchoice> 4\n2 to 6 numbers, separated by spaces> 1,5 2\nType 2 to 6 numbers such as 3, -2.5 or 1e16, separated by spaces.\n\n1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit\nchoice> 4\n2 to 6 numbers, separated by spaces> 123456789012345678 1\nType 2 to 6 numbers such as 3, -2.5 or 1e16, separated by spaces.\n\n1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit\nchoice> 4\n2 to 6 numbers, separated by spaces> -2.5 1e-3 7E+2\nIn 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.\n\n1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit\nchoice> 5\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "1\n2\n3\n4\n1e16 3 -1e16 2\n4\n0.1 0.2 0.3\n4\n1 2\n5\n",
      "screen": "== Float sums ==\nPartial sums added as they arrive, and what the total depends on.\n\n1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit\nchoice> 1\nFour workers sum a day's account movements, and the reducer adds their partials as they arrive:\n  inflows      1e+16\n  outflows    -1e+16\n  fees           1.0\n  interest       0.5\nThe true total is 1.5: the inflows and the outflows cancel.\n\n1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit\nchoice> 2\n  arrival order                           loop   Kahan  Neumaier\n  inflows, outflows, fees, interest        1.5     1.5       1.5\n  inflows, outflows, interest, fees        1.5     1.5       1.5\n  inflows, fees, outflows, interest        0.5     0.5       1.5\n  inflows, fees, interest, outflows        0.0     2.0       1.5\n  inflows, interest, outflows, fees        1.0     1.0       1.5\n  inflows, interest, fees, outflows        0.0     2.0       1.5\n  outflows, inflows, fees, interest        1.5     1.5       1.5\n  outflows, inflows, interest, fees        1.5     1.5       1.5\n  outflows, fees, inflows, interest        0.5     0.5       1.5\n  outflows, fees, interest, inflows        0.0     2.0       1.5\n  outflows, interest, inflows, fees        1.0     1.0       1.5\n  outflows, interest, fees, inflows        0.0     2.0       1.5\n  fees, inflows, outflows, interest        0.5     0.5       1.5\n  fees, inflows, interest, outflows        0.0     0.0       1.5\n  fees, outflows, inflows, interest        0.5     0.5       1.5\n  fees, outflows, interest, inflows        0.0     0.0       1.5\n  fees, interest, inflows, outflows        2.0     2.0       1.5\n  fees, interest, outflows, inflows        2.0     2.0       1.5\n  interest, inflows, outflows, fees        1.0     1.0       1.5\n  interest, inflows, fees, outflows        0.0     0.0       1.5\n  interest, outflows, inflows, fees        1.0     1.0       1.5\n  interest, outflows, fees, inflows        0.0     0.0       1.5\n  interest, fees, inflows, outflows        2.0     2.0       1.5\n  interest, fees, outflows, inflows        2.0     2.0       1.5\nThe loop: 5 different totals, and 1.5 in 4 of the 24 orders. The answer is a function of which worker finished first.\nKahan'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.\nNeumaier'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.\n\n1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit\nchoice> 3\nTen values of 0.1, added one by one:\n  values  loop                  Kahan                 Neumaier\n  1       0.1                   0.1                   0.1\n  2       0.2                   0.2                   0.2\n  3       0.30000000000000004   0.30000000000000004   0.30000000000000004\n  4       0.4                   0.4                   0.4\n  5       0.5                   0.5                   0.5\n  6       0.6                   0.6000000000000001    0.6000000000000001\n  7       0.7                   0.7000000000000001    0.7000000000000001\n  8       0.7999999999999999    0.8                   0.8\n  9       0.8999999999999999    0.9                   0.9\n  10      0.9999999999999999    1.0                   1.0\nThe 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.\n\n1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit\nchoice> 4\n2 to 6 numbers, separated by spaces> 1e16 3 -1e16 2\nIn 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.\n\n1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit\nchoice> 4\n2 to 6 numbers, separated by spaces> 0.1 0.2 0.3\nIn 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.\n\n1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit\nchoice> 4\n2 to 6 numbers, separated by spaces> 1 2\nIn 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.\n\n1) the partials  2) every arrival order  3) ten values of 0.1  4) numbers of your own  5) quit\nchoice> 5\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "the-order-the-workers-finished-changed-the-total",
      "caseId": "469-the-order-the-workers-finished-changed-the-total",
      "title": "The order the workers finished changed the total"
    },
    {
      "slug": "the-compensated-sum-and-the-loop-disagree",
      "caseId": "463-the-compensated-sum-and-the-loop-disagree",
      "title": "The compensated sum and the loop disagree"
    }
  ],
  "updated": "2026-10-19"
}
