mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
array.c, enum.c: change sum algorithm
* array.c (rb_ary_sum): change the algorithm to Kahan-Babuska balancing summation to be more precise. [Feature #12871] [ruby-core:77771] * enum.c (sum_iter, enum_sum): ditto. * test_array.rb, test_enum.rb: add an assertion for the above change. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@57001 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
0cb1a2b812
commit
48f5f5915b
4 changed files with 24 additions and 11 deletions
16
array.c
16
array.c
|
@ -5796,14 +5796,17 @@ rb_ary_sum(int argc, VALUE *argv, VALUE ary)
|
|||
}
|
||||
|
||||
if (RB_FLOAT_TYPE_P(e)) {
|
||||
/* Kahan's compensated summation algorithm */
|
||||
/*
|
||||
* Kahan-Babuska balancing compensated summation algorithm
|
||||
* See http://link.springer.com/article/10.1007/s00607-005-0139-x
|
||||
*/
|
||||
double f, c;
|
||||
|
||||
f = NUM2DBL(v);
|
||||
c = 0.0;
|
||||
goto has_float_value;
|
||||
for (; i < RARRAY_LEN(ary); i++) {
|
||||
double x, y, t;
|
||||
double x, t;
|
||||
e = RARRAY_AREF(ary, i);
|
||||
if (block_given)
|
||||
e = rb_yield(e);
|
||||
|
@ -5819,11 +5822,14 @@ rb_ary_sum(int argc, VALUE *argv, VALUE ary)
|
|||
else
|
||||
goto not_float;
|
||||
|
||||
y = x - c;
|
||||
t = f + y;
|
||||
c = (t - f) - y;
|
||||
t = f + x;
|
||||
if (fabs(f) >= fabs(x))
|
||||
c += ((f - t) + x);
|
||||
else
|
||||
c += ((x - t) + f);
|
||||
f = t;
|
||||
}
|
||||
f += c;
|
||||
return DBL2NUM(f);
|
||||
|
||||
not_float:
|
||||
|
|
17
enum.c
17
enum.c
|
@ -3644,8 +3644,11 @@ sum_iter(VALUE i, struct enum_sum_memo *memo)
|
|||
}
|
||||
}
|
||||
else if (RB_FLOAT_TYPE_P(v)) {
|
||||
/* Kahan's compensated summation algorithm */
|
||||
double x, y, t;
|
||||
/*
|
||||
* Kahan-Babuska balancing compensated summation algorithm
|
||||
* See http://link.springer.com/article/10.1007/s00607-005-0139-x
|
||||
*/
|
||||
double x, t;
|
||||
|
||||
float_value:
|
||||
if (RB_FLOAT_TYPE_P(i))
|
||||
|
@ -3662,9 +3665,11 @@ sum_iter(VALUE i, struct enum_sum_memo *memo)
|
|||
goto some_value;
|
||||
}
|
||||
|
||||
y = x - c;
|
||||
t = f + y;
|
||||
c = (t - f) - y;
|
||||
t = f + x;
|
||||
if (fabs(f) >= fabs(x))
|
||||
c += ((f - t) + x);
|
||||
else
|
||||
c += ((x - t) + f);
|
||||
f = t;
|
||||
}
|
||||
else {
|
||||
|
@ -3788,7 +3793,7 @@ enum_sum(int argc, VALUE* argv, VALUE obj)
|
|||
rb_block_call(obj, id_each, 0, 0, enum_sum_i, (VALUE)&memo);
|
||||
|
||||
if (memo.float_value) {
|
||||
return DBL2NUM(memo.f);
|
||||
return DBL2NUM(memo.f + memo.c);
|
||||
}
|
||||
else {
|
||||
if (memo.n != 0)
|
||||
|
|
|
@ -2821,6 +2821,7 @@ class TestArray < Test::Unit::TestCase
|
|||
assert_float_equal(large_number+(small_number*10), [large_number, *[small_number]*10].sum)
|
||||
assert_float_equal(large_number+(small_number*10), [large_number/1r, *[small_number]*10].sum)
|
||||
assert_float_equal(large_number+(small_number*11), [small_number, large_number/1r, *[small_number]*10].sum)
|
||||
assert_float_equal(small_number, [large_number, small_number, -large_number].sum)
|
||||
|
||||
assert_equal("abc", ["a", "b", "c"].sum(""))
|
||||
assert_equal([1, [2], 3], [[1], [[2]], [3]].sum([]))
|
||||
|
|
|
@ -903,6 +903,7 @@ class TestEnumerable < Test::Unit::TestCase
|
|||
assert_float_equal(large_number+(small_number*10), [large_number, *[small_number]*10].each.sum)
|
||||
assert_float_equal(large_number+(small_number*10), [large_number/1r, *[small_number]*10].each.sum)
|
||||
assert_float_equal(large_number+(small_number*11), [small_number, large_number/1r, *[small_number]*10].each.sum)
|
||||
assert_float_equal(small_number, [large_number, small_number, -large_number].each.sum)
|
||||
|
||||
assert_equal("abc", ["a", "b", "c"].each.sum(""))
|
||||
assert_equal([1, [2], 3], [[1], [[2]], [3]].each.sum([]))
|
||||
|
|
Loading…
Add table
Reference in a new issue