_sum(data) -> (type, sum, count) Return a high-precision sum of the given numeric data as a fraction, together with the type to be converted to and the count of items. Examples -------- >>> _sum([3, 2.25, 4.5, -0.5, 0.25]) ( , Fraction(19, 2), 5) Some so
(data)
| 1451 | ## Private utilities ####################################################### |
| 1452 | |
| 1453 | def _sum(data): |
| 1454 | """_sum(data) -> (type, sum, count) |
| 1455 | |
| 1456 | Return a high-precision sum of the given numeric data as a fraction, |
| 1457 | together with the type to be converted to and the count of items. |
| 1458 | |
| 1459 | Examples |
| 1460 | -------- |
| 1461 | |
| 1462 | >>> _sum([3, 2.25, 4.5, -0.5, 0.25]) |
| 1463 | (<class 'float'>, Fraction(19, 2), 5) |
| 1464 | |
| 1465 | Some sources of round-off error will be avoided: |
| 1466 | |
| 1467 | # Built-in sum returns zero. |
| 1468 | >>> _sum([1e50, 1, -1e50] * 1000) |
| 1469 | (<class 'float'>, Fraction(1000, 1), 3000) |
| 1470 | |
| 1471 | Fractions and Decimals are also supported: |
| 1472 | |
| 1473 | >>> from fractions import Fraction as F |
| 1474 | >>> _sum([F(2, 3), F(7, 5), F(1, 4), F(5, 6)]) |
| 1475 | (<class 'fractions.Fraction'>, Fraction(63, 20), 4) |
| 1476 | |
| 1477 | >>> from decimal import Decimal as D |
| 1478 | >>> data = [D("0.1375"), D("0.2108"), D("0.3061"), D("0.0419")] |
| 1479 | >>> _sum(data) |
| 1480 | (<class 'decimal.Decimal'>, Fraction(6963, 10000), 4) |
| 1481 | |
| 1482 | Mixed types are currently treated as an error, except that int is |
| 1483 | allowed. |
| 1484 | |
| 1485 | """ |
| 1486 | count = 0 |
| 1487 | types = set() |
| 1488 | types_add = types.add |
| 1489 | partials = {} |
| 1490 | partials_get = partials.get |
| 1491 | |
| 1492 | for typ, values in groupby(data, type): |
| 1493 | types_add(typ) |
| 1494 | for n, d in map(_exact_ratio, values): |
| 1495 | count += 1 |
| 1496 | partials[d] = partials_get(d, 0) + n |
| 1497 | |
| 1498 | if None in partials: |
| 1499 | # The sum will be a NAN or INF. We can ignore all the finite |
| 1500 | # partials, and just look at this special one. |
| 1501 | total = partials[None] |
| 1502 | assert not _isfinite(total) |
| 1503 | else: |
| 1504 | # Sum all the partial sums using builtin sum. |
| 1505 | total = sum(Fraction(n, d) for d, n in partials.items()) |
| 1506 | |
| 1507 | T = reduce(_coerce, types, int) # or raise TypeError |
| 1508 | return (T, total, count) |
| 1509 | |
| 1510 |