sums.nim 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2019 b3liever
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. ## Fast sumation functions.
  9. func sumKbn*[T](x: openArray[T]): T =
  10. ## Kahan (compensated) summation: O(1) error growth, at the expense
  11. ## of a considerable increase in computational expense.
  12. if len(x) == 0: return
  13. var sum = x[0]
  14. var c = T(0)
  15. for i in 1 ..< len(x):
  16. let xi = x[i]
  17. let t = sum + xi
  18. if abs(sum) >= abs(xi):
  19. c += (sum - t) + xi
  20. else:
  21. c += (xi - t) + sum
  22. sum = t
  23. result = sum + c
  24. func sumPairwise[T](x: openArray[T], i0, n: int): T =
  25. if n < 128:
  26. result = x[i0]
  27. for i in i0 + 1 ..< i0 + n:
  28. result += x[i]
  29. else:
  30. let n2 = n div 2
  31. result = sumPairwise(x, i0, n2) + sumPairwise(x, i0 + n2, n - n2)
  32. func sumPairs*[T](x: openArray[T]): T =
  33. ## Pairwise (cascade) summation of ``x[i0:i0+n-1]``, with O(log n) error growth
  34. ## (vs O(n) for a simple loop) with negligible performance cost if
  35. ## the base case is large enough.
  36. ##
  37. ## See, e.g.:
  38. ## * http://en.wikipedia.org/wiki/Pairwise_summation
  39. ## Higham, Nicholas J. (1993), "The accuracy of floating point
  40. ## summation", SIAM Journal on Scientific Computing 14 (4): 783–799.
  41. ##
  42. ## In fact, the root-mean-square error growth, assuming random roundoff
  43. ## errors, is only O(sqrt(log n)), which is nearly indistinguishable from O(1)
  44. ## in practice. See:
  45. ## * Manfred Tasche and Hansmartin Zeuner, Handbook of
  46. ## Analytic-Computational Methods in Applied Mathematics (2000).
  47. ##
  48. let n = len(x)
  49. if n == 0: T(0) else: sumPairwise(x, 0, n)
  50. when isMainModule:
  51. from math import pow
  52. var epsilon = 1.0
  53. while 1.0 + epsilon != 1.0:
  54. epsilon /= 2.0
  55. let data = @[1.0, epsilon, -epsilon]
  56. assert sumKbn(data) == 1.0
  57. assert sumPairs(data) != 1.0 # known to fail
  58. assert (1.0 + epsilon) - epsilon != 1.0
  59. var tc1: seq[float]
  60. for n in 1 .. 1000:
  61. tc1.add 1.0 / n.float
  62. assert sumKbn(tc1) == 7.485470860550345
  63. assert sumPairs(tc1) == 7.485470860550345
  64. var tc2: seq[float]
  65. for n in 1 .. 1000:
  66. tc2.add pow(-1.0, n.float) / n.float
  67. assert sumKbn(tc2) == -0.6926474305598203
  68. assert sumPairs(tc2) == -0.6926474305598204