_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)
(data)
| 152 | # === Private utilities === |
| 153 | |
| 154 | def _sum(data): |
| 155 | """_sum(data) -> (type, sum, count) |
| 156 | |
| 157 | Return a high-precision sum of the given numeric data as a fraction, |
| 158 | together with the type to be converted to and the count of items. |
| 159 | |
| 160 | Examples |
| 161 | -------- |
| 162 | |
| 163 | >>> _sum([3, 2.25, 4.5, -0.5, 0.25]) |
| 164 | (<class 'float'>, Fraction(19, 2), 5) |
| 165 | |
| 166 | Some sources of round-off error will be avoided: |
| 167 | |
| 168 | # Built-in sum returns zero. |
| 169 | >>> _sum([1e50, 1, -1e50] * 1000) |
| 170 | (<class 'float'>, Fraction(1000, 1), 3000) |
| 171 | |
| 172 | Fractions and Decimals are also supported: |
| 173 | |
| 174 | >>> from fractions import Fraction as F |
| 175 | >>> _sum([F(2, 3), F(7, 5), F(1, 4), F(5, 6)]) |
| 176 | (<class 'fractions.Fraction'>, Fraction(63, 20), 4) |
| 177 | |
| 178 | >>> from decimal import Decimal as D |
| 179 | >>> data = [D("0.1375"), D("0.2108"), D("0.3061"), D("0.0419")] |
| 180 | >>> _sum(data) |
| 181 | (<class 'decimal.Decimal'>, Fraction(6963, 10000), 4) |
| 182 | |
| 183 | Mixed types are currently treated as an error, except that int is |
| 184 | allowed. |
| 185 | """ |
| 186 | count = 0 |
| 187 | types = set() |
| 188 | types_add = types.add |
| 189 | partials = {} |
| 190 | partials_get = partials.get |
| 191 | for typ, values in groupby(data, type): |
| 192 | types_add(typ) |
| 193 | for n, d in map(_exact_ratio, values): |
| 194 | count += 1 |
| 195 | partials[d] = partials_get(d, 0) + n |
| 196 | if None in partials: |
| 197 | # The sum will be a NAN or INF. We can ignore all the finite |
| 198 | # partials, and just look at this special one. |
| 199 | total = partials[None] |
| 200 | assert not _isfinite(total) |
| 201 | else: |
| 202 | # Sum all the partial sums using builtin sum. |
| 203 | total = sum(Fraction(n, d) for d, n in partials.items()) |
| 204 | T = reduce(_coerce, types, int) # or raise TypeError |
| 205 | return (T, total, count) |
| 206 | |
| 207 | |
| 208 | def _ss(data, c=None): |