diff --git a/ChangeLog b/ChangeLog index 924a218a8c..b20489690c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +Wed Dec 13 02:00:00 2013 Kenta Murata + + * ext/bigdecimal/bigdecimal.c (VpSetPTR): fix for limitation of the resulting + precision. + + * test/bigdecimal/test_bigdecimal.rb (test_limit): add tests for the above + change. + Wed Dec 13 01:56:00 2013 Kenta Murata * ext/bigdecimal/bigdecimal.c (VpAddAbs): put out a conditional branch from diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index 266983c785..e0b7c01e08 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -4189,6 +4189,7 @@ VpAddAbs(Real *a, Real *b, Real *c) a_pos = ap; b_pos = bp; c_pos = cp; + if (word_shift == (size_t)-1L) return 0; /* Overflow */ if (b_pos == (size_t)-1L) goto Assign_a; @@ -4380,12 +4381,19 @@ static size_t VpSetPTR(Real *a, Real *b, Real *c, size_t *a_pos, size_t *b_pos, size_t *c_pos, BDIGIT *av, BDIGIT *bv) { size_t left_word, right_word, word_shift; + + size_t const round_limit = (VpGetPrecLimit() + BASE_FIG - 1) / BASE_FIG; + + assert(a->exponent >= b->expoennt); + c->frac[0] = 0; *av = *bv = 0; + word_shift = (a->exponent - b->exponent); left_word = b->Prec + word_shift; right_word = Max(a->Prec, left_word); left_word = c->MaxPrec - 1; /* -1 ... prepare for round up */ + /* * check if 'round' is needed. */ @@ -4408,7 +4416,9 @@ VpSetPTR(Real *a, Real *b, Real *c, size_t *a_pos, size_t *b_pos, size_t *c_pos, * a_pos = | */ *a_pos = left_word; - *av = a->frac[*a_pos]; /* av is 'A' shown in above. */ + if (*a_pos <= round_limit) { + *av = a->frac[*a_pos]; /* av is 'A' shown in above. */ + } } else { /* @@ -4427,7 +4437,9 @@ VpSetPTR(Real *a, Real *b, Real *c, size_t *a_pos, size_t *b_pos, size_t *c_pos, */ if (c->MaxPrec >= word_shift + 1) { *b_pos = c->MaxPrec - word_shift - 1; - *bv = b->frac[*b_pos]; + if (*b_pos + word_shift <= round_limit) { + *bv = b->frac[*b_pos]; + } } else { *b_pos = -1L; diff --git a/test/bigdecimal/test_bigdecimal.rb b/test/bigdecimal/test_bigdecimal.rb index e7bb1c513d..6fdc422fdf 100644 --- a/test/bigdecimal/test_bigdecimal.rb +++ b/test/bigdecimal/test_bigdecimal.rb @@ -1151,6 +1151,25 @@ class TestBigDecimal < Test::Unit::TestCase assert_equal(90, x ** 4) # OK? must it be 80? # 3 * 3 * 3 * 3 = 10 * 3 * 3 = 30 * 3 = 90 ??? assert_raise(ArgumentError) { BigDecimal.limit(-1) } + + bug7458 = '[ruby-core:50269] [#7458]' + one = BigDecimal('1') + epsilon = BigDecimal('0.7E-18') + BigDecimal.save_limit do + BigDecimal.limit(0) + assert_equal(BigDecimal("1.0000000000000000007"), one + epsilon, "limit(0) #{bug7458}") + + 1.upto(18) do |lim| + BigDecimal.limit(lim) + assert_equal(BigDecimal("1.0"), one + epsilon, "limit(#{lim}) #{bug7458}") + end + + BigDecimal.limit(19) + assert_equal(BigDecimal("1.000000000000000001"), one + epsilon, "limit(19) #{bug7458}") + + BigDecimal.limit(20) + assert_equal(BigDecimal("1.0000000000000000007"), one + epsilon, "limit(20) #{bug7458}") + end end def test_sign