From: t_nissie@... Date: 2016-12-01T16:56:29+00:00 Subject: [ruby-core:78459] [Ruby trunk Feature#12871] Using the algorithm like math.fsum of Python for Array#sum Issue #12871 has been updated by Takeshi Nishimatsu. A quick hack. * Elongation (or reallocation) of the array of partials[] when nn exeeds NUM_PARTIALS. * Tests. * Name of this algorithm. Kahan-Babuska-Neumaier? are required. ~~~ diff diff --git a/array.c b/array.c index b99ab45..2b818bf 100644 --- a/array.c +++ b/array.c @@ -5688,6 +5688,8 @@ rb_ary_dig(int argc, VALUE *argv, VALUE self) return rb_obj_dig(argc, argv, self, Qnil); } +#define NUM_PARTIALS 32 /* initial partials array size, on stack */ + /* * call-seq: * ary.sum(init=0) -> number @@ -5796,14 +5798,15 @@ rb_ary_sum(int argc, VALUE *argv, VALUE ary) } if (RB_FLOAT_TYPE_P(e)) { - /* Kahan's compensated summation algorithm */ - double f, c; + /* ???'s compensated summation algorithm */ + double f,partials[NUM_PARTIALS]; + long ii, jj, nn; - f = NUM2DBL(v); - c = 0.0; + partials[0] = NUM2DBL(v); + nn=1; goto has_float_value; for (; i < RARRAY_LEN(ary); i++) { - double x, y, t; + double x, y, tmp, hi, lo; e = RARRAY_AREF(ary, i); if (block_given) e = rb_yield(e); @@ -5819,10 +5822,28 @@ rb_ary_sum(int argc, VALUE *argv, VALUE ary) else goto not_float; - y = x - c; - t = f + y; - c = (t - f) - y; - f = t; + ii = 0; + for (jj=0; jj < nn; jj++) { + y = partials[jj]; + if (fabs(x) < fabs(y)) { + tmp = x; + x = y; + y =tmp; + } + hi = x + y; /* upper bits */ + lo = y - (hi - x); /* lower bits (lost) */ + if (lo != 0.0) { + partials[ii] = lo; + ii += 1; + } + x = hi; + } + partials[ii] = x; + nn = ii+1; + } + f = 0.0; + for (i=0; i 0.0 (expected 0.0000000001) ~~~ Python's math.fsum uses another algorithm. It saves all digits, and returns accurate value in such a case. (See: https://github.com/python/cpython/blob/d267006f18592165ed97e0a9c2494d3bce25fc2b/Modules/mathmodule.c#L1087) ~~~ python # python 3.5.2 from math import fsum fsum([10000000000.0, 0.0000000001, -10000000000.0]) #=> 0.0000000001 ~~~ I propose to use the algorithm like math.fsum of Python for Array#sum. This is an example implementation in Ruby. ~~~ ruby class Array # This implementation does not consider non float values def sum partials = [] each do |x| i = 0 partials.each do |y| x, y = y, x if x.abs < y.abs hi = x + y # upper bits lo = y - (hi - x) # lower bits (lost) if lo != 0.0 partials[i] = lo i += 1 end x = hi end partials[i..-1] = [x] end partials.inject(0.0, :+) end end ~~~ -- https://bugs.ruby-lang.org/ Unsubscribe: