aboutsummaryrefslogtreecommitdiffstats
path: root/array.c
diff options
context:
space:
mode:
authorakr <akr@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2016-04-13 13:51:53 +0000
committerakr <akr@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2016-04-13 13:51:53 +0000
commitb359d20352163558d4e7550714844f24b568297e (patch)
treeb5932236ea53fb4dc0e1ec1c283566157170c263 /array.c
parentc25853518665c71a79704ca50d40bf2fc4bc6aaa (diff)
downloadruby-b359d20352163558d4e7550714844f24b568297e.tar.gz
* array.c (rb_ary_sum): Array#sum is implemented.
Kahan's compensated summation algorithm for precise sum of float numbers is moved from ary_inject_op in enum.c. * enum.c (ary_inject_op): Don't specialize for float numbers. [ruby-core:74569] [Feature#12217] proposed by mrkn. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@54565 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'array.c')
-rw-r--r--array.c87
1 files changed, 87 insertions, 0 deletions
diff --git a/array.c b/array.c
index 7a01dbc984..85e3177b21 100644
--- a/array.c
+++ b/array.c
@@ -5651,6 +5651,92 @@ rb_ary_dig(int argc, VALUE *argv, VALUE self)
}
/*
+ * call-seq:
+ * ary.sum -> number
+ *
+ * Returns the sum of elements.
+ * For example, [e1, e2, e3].sum returns 0 + e1 + e2 + e3.
+ *
+ * If <i>ary</i> is empty, it returns 0.
+ *
+ * [].sum #=> 0
+ * [1, 2, 3].sum #=> 6
+ * [3, 5.5].sum #=> 8.5
+ *
+ * This method may not respect method redefinition of "+" methods
+ * such as Fixnum#+.
+ *
+ */
+
+VALUE
+rb_ary_sum(VALUE ary)
+{
+ VALUE v, e;
+ long i, n;
+
+ if (RARRAY_LEN(ary) == 0)
+ return LONG2FIX(0);
+
+ v = LONG2FIX(0);
+
+ n = 0;
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
+ e = RARRAY_AREF(ary, i);
+ if (FIXNUM_P(e)) {
+ n += FIX2LONG(e); /* should not overflow long type */
+ if (!FIXABLE(n)) {
+ v = rb_big_plus(LONG2NUM(n), v);
+ n = 0;
+ }
+ }
+ else if (RB_TYPE_P(e, T_BIGNUM))
+ v = rb_big_plus(e, v);
+ else
+ goto not_integer;
+ }
+ if (n != 0)
+ v = rb_fix_plus(LONG2FIX(n), v);
+ return v;
+
+ not_integer:
+ if (n != 0)
+ v = rb_fix_plus(LONG2FIX(n), v);
+
+ if (RB_FLOAT_TYPE_P(e)) {
+ /* Kahan's compensated summation algorithm */
+ double f, c;
+ f = NUM2DBL(v);
+ c = 0.0;
+ for (; i < RARRAY_LEN(ary); i++) {
+ double x, y, t;
+ e = RARRAY_AREF(ary, i);
+ if (RB_FLOAT_TYPE_P(e))
+ x = RFLOAT_VALUE(e);
+ else if (FIXNUM_P(e))
+ x = FIX2LONG(e);
+ else if (RB_TYPE_P(e, T_BIGNUM))
+ x = rb_big2dbl(e);
+ else
+ goto not_float;
+
+ y = x - c;
+ t = f + y;
+ c = (t - f) - y;
+ f = t;
+ }
+ return DBL2NUM(f);
+
+ not_float:
+ v = DBL2NUM(f);
+ }
+
+ for (; i < RARRAY_LEN(ary); i++) {
+ v = rb_funcall(v, idPLUS, 1, RARRAY_AREF(ary, i));
+ }
+ return v;
+}
+
+/*
* Arrays are ordered, integer-indexed collections of any object.
*
* Array indexing starts at 0, as in C or Java. A negative index is assumed
@@ -6005,6 +6091,7 @@ Init_Array(void)
rb_define_method(rb_cArray, "bsearch_index", rb_ary_bsearch_index, 0);
rb_define_method(rb_cArray, "any?", rb_ary_any_p, 0);
rb_define_method(rb_cArray, "dig", rb_ary_dig, -1);
+ rb_define_method(rb_cArray, "sum", rb_ary_sum, 0);
id_cmp = rb_intern("<=>");
id_random = rb_intern("random");