diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index 99bd9f06..6846bcb2 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -5123,6 +5123,7 @@ VpNmlz(Real *a) NoVal: a->frac[0] = 0; a->Prec = 1; + a->exponent = 0; return 0; } @@ -6007,7 +6008,7 @@ VpMidRound(Real *y, unsigned short f, ssize_t nf) y->frac[ix] = div; VpNmlz(y); } - if (exptoadd > 0) { + if (exptoadd > 0 && !VpIsZero(y)) { y->exponent += (SIGNED_VALUE)(exptoadd / BASE_FIG); exptoadd %= (ssize_t)BASE_FIG; for (i = 0; i < exptoadd; i++) { diff --git a/ext/bigdecimal/bigdecimal.h b/ext/bigdecimal/bigdecimal.h index faa66264..46fb36b1 100644 --- a/ext/bigdecimal/bigdecimal.h +++ b/ext/bigdecimal/bigdecimal.h @@ -267,21 +267,21 @@ VP_EXPORT inline BDVALUE rbd_allocate_struct_zero_wrap(int sign, size_t const di #define VpIsPosZero(a) ((a)->sign==VP_SIGN_POSITIVE_ZERO) #define VpIsNegZero(a) ((a)->sign==VP_SIGN_NEGATIVE_ZERO) #define VpIsZero(a) (VpIsPosZero(a) || VpIsNegZero(a)) -#define VpSetPosZero(a) ((a)->frac[0]=0,(a)->Prec=1,(a)->sign=VP_SIGN_POSITIVE_ZERO) -#define VpSetNegZero(a) ((a)->frac[0]=0,(a)->Prec=1,(a)->sign=VP_SIGN_NEGATIVE_ZERO) +#define VpSetPosZero(a) ((a)->frac[0]=0,(a)->Prec=1,(a)->exponent=0,(a)->sign=VP_SIGN_POSITIVE_ZERO) +#define VpSetNegZero(a) ((a)->frac[0]=0,(a)->Prec=1,(a)->exponent=0,(a)->sign=VP_SIGN_NEGATIVE_ZERO) #define VpSetZero(a,s) (void)(((s)>0)?VpSetPosZero(a):VpSetNegZero(a)) /* NaN */ #define VpIsNaN(a) ((a)->sign==VP_SIGN_NaN) -#define VpSetNaN(a) ((a)->frac[0]=0,(a)->Prec=1,(a)->sign=VP_SIGN_NaN) +#define VpSetNaN(a) ((a)->frac[0]=0,(a)->Prec=1,(a)->exponent=0,(a)->sign=VP_SIGN_NaN) /* Infinity */ #define VpIsPosInf(a) ((a)->sign==VP_SIGN_POSITIVE_INFINITE) #define VpIsNegInf(a) ((a)->sign==VP_SIGN_NEGATIVE_INFINITE) #define VpIsInf(a) (VpIsPosInf(a) || VpIsNegInf(a)) #define VpIsDef(a) ( !(VpIsNaN(a)||VpIsInf(a)) ) -#define VpSetPosInf(a) ((a)->frac[0]=0,(a)->Prec=1,(a)->sign=VP_SIGN_POSITIVE_INFINITE) -#define VpSetNegInf(a) ((a)->frac[0]=0,(a)->Prec=1,(a)->sign=VP_SIGN_NEGATIVE_INFINITE) +#define VpSetPosInf(a) ((a)->frac[0]=0,(a)->Prec=1,(a)->exponent=0,(a)->sign=VP_SIGN_POSITIVE_INFINITE) +#define VpSetNegInf(a) ((a)->frac[0]=0,(a)->Prec=1,(a)->exponent=0,(a)->sign=VP_SIGN_NEGATIVE_INFINITE) #define VpSetInf(a,s) (void)(((s)>0)?VpSetPosInf(a):VpSetNegInf(a)) #define VpHasVal(a) (a->frac[0]) #define VpIsOne(a) ((a->Prec==1)&&(a->frac[0]==1)&&(a->exponent==1)) diff --git a/ext/bigdecimal/div.h b/ext/bigdecimal/div.h index a171a471..c2160a3d 100644 --- a/ext/bigdecimal/div.h +++ b/ext/bigdecimal/div.h @@ -60,14 +60,10 @@ divmod_by_inv_mul(VALUE x, VALUE y, VALUE inv, VALUE *res_div, VALUE *res_mod) { static void slice_copy(DECDIG *dest, Real *src, size_t rshift, size_t length) { ssize_t start = src->exponent - (ssize_t)rshift - (ssize_t)length; - if (start >= (ssize_t)src->Prec) return; - if (start < 0) { - dest -= start; - length -= (size_t)(-start); - start = 0; - } - size_t max_length = (size_t)((ssize_t)src->Prec - start); - memcpy(dest, src->frac + start, Min(length, max_length) * sizeof(DECDIG)); + ssize_t from = Max(start, 0); + ssize_t to = Min(start + (ssize_t)length, (ssize_t)src->Prec); + if (from >= to) return; + memcpy(dest + (from - start), src->frac + from, (size_t)(to - from) * sizeof(DECDIG)); } /* Calculates divmod using Newton-Raphson method. @@ -162,11 +158,8 @@ VpDivdNewtonInner(VALUE args_ptr) r2 = GetBDValueMust(mod); VpAsgn(c, c2.real, VpGetSign(a) * VpGetSign(b)); VpAsgn(r, r2.real, VpGetSign(a)); - AddExponent(c, a->exponent); - AddExponent(c, -b->exponent); - AddExponent(c, -(ssize_t)div_prec); - AddExponent(r, a->exponent); - AddExponent(r, -(ssize_t)(base_prec + div_prec)); + if (!VpIsZero(c)) AddExponent(c, a->exponent - b->exponent - (ssize_t)div_prec); + if (!VpIsZero(r)) AddExponent(r, a->exponent - (ssize_t)(base_prec + div_prec)); RB_GC_GUARD(a2.bigdecimal); RB_GC_GUARD(b2.bigdecimal); RB_GC_GUARD(c2.bigdecimal); diff --git a/test/bigdecimal/test_vp_operation.rb b/test/bigdecimal/test_vp_operation.rb index ce690aee..eea63b5e 100644 --- a/test/bigdecimal/test_vp_operation.rb +++ b/test/bigdecimal/test_vp_operation.rb @@ -182,6 +182,17 @@ def test_vpdivd_large_prec_divisor assert_vpdivd_equal([divy2_2, x - y2 * divy2_2], [x, y2, 2]) end + def test_vpdivd_zero_quotient_block + # The last block of the quotient is zero and x * inv is far below 1. + # Copying that zero quotient wrote outside of the result buffer. + y = BigDecimal(7 * BASE + 1) + x = y * BASE**3 + 5 + assert_vpdivd_equal([BigDecimal(BASE**3), BigDecimal(5)], [x, y, 4]) + y = BigDecimal(10**1000 + 7) + x = y * BigDecimal("1e1800") + 5 + assert_equal([BigDecimal("1e1800"), BigDecimal(5)], x.divmod(y)) + end + def test_vpdivd_intermediate_zero x = BigDecimal('123456789.246913578000000000123456789') y = BigDecimal('123456789')