@@ -174,6 +174,14 @@ check_allocation_count_nonzero(void)
174174# define check_allocation_count_nonzero () /* nothing */
175175#endif /* BIGDECIMAL_DEBUG */
176176
177+ /* VpMult VpDivd helpers */
178+ #define VPMULT_RESULT_PREC (a , b ) (a->Prec + b->Prec + 1)
179+ /* To calculate VpDivd with n-digits precision, quotient needs n+2*BASE_FIG-1 digits space */
180+ /* In the worst precision case 0001_1111_1111 / 9999 = 0000_0001_1112, there are 2*BASE_FIG-1 leading zeros */
181+ #define VPDIVD_QUO_DIGITS (required_digits ) ((required_digits) + 2 * BASE_FIG - 1)
182+ /* Required r.MaxPrec for calculating VpDivd(c, r, a, b) */
183+ #define VPDIVD_REM_PREC (a , b , c ) Max(a->Prec, b->Prec + c->MaxPrec - 1)
184+
177185static NULLABLE_BDVALUE
178186CreateFromString (size_t mx , const char * str , VALUE klass , bool strict_p , bool raise_exception );
179187
@@ -222,16 +230,6 @@ rbd_allocate_struct_decimal_digits(size_t const decimal_digits, bool limit_preci
222230
223231static VALUE BigDecimal_wrap_struct (VALUE obj , Real * vp );
224232
225- static BDVALUE
226- rbd_reallocate_struct (BDVALUE value , size_t const internal_digits )
227- {
228- size_t const size = rbd_struct_size (internal_digits );
229- Real * new_real = (Real * )ruby_xrealloc (value .real , size );
230- new_real -> MaxPrec = internal_digits ;
231- BigDecimal_wrap_struct (value .bigdecimal , new_real );
232- return (BDVALUE ) { value .bigdecimal , new_real };
233- }
234-
235233static void
236234rbd_free_struct (Real * real )
237235{
@@ -1843,7 +1841,6 @@ BigDecimal_mult(VALUE self, VALUE r)
18431841{
18441842 ENTER (5 );
18451843 BDVALUE a , b , c ;
1846- size_t mx ;
18471844
18481845 GUARD_OBJ (a , GetBDValueMust (self ));
18491846 if (RB_TYPE_P (r , T_FLOAT )) {
@@ -1859,8 +1856,7 @@ BigDecimal_mult(VALUE self, VALUE r)
18591856 b = bdvalue_nonnullable (b2 );
18601857 }
18611858
1862- mx = a .real -> Prec + b .real -> Prec ;
1863- GUARD_OBJ (c , NewZeroWrapLimited (1 , (mx + 1 ) * BASE_FIG ));
1859+ GUARD_OBJ (c , NewZeroWrapLimited (1 , VPMULT_RESULT_PREC (a .real , b .real ) * BASE_FIG ));
18641860 VpMult (c .real , a .real , b .real );
18651861 return CheckGetValue (c );
18661862}
@@ -1941,9 +1937,9 @@ static VALUE
19411937BigDecimal_DoDivmod (VALUE self , VALUE r , NULLABLE_BDVALUE * div , NULLABLE_BDVALUE * mod , bool truncate )
19421938{
19431939 ENTER (8 );
1944- BDVALUE a , b , c , d , e , res ;
1945- ssize_t a_prec , b_prec ;
1946- size_t mx ;
1940+ BDVALUE a , b , dv , md , res ;
1941+ ssize_t a_exponent , b_exponent ;
1942+ size_t mx , rx ;
19471943
19481944 GUARD_OBJ (a , GetBDValueMust (self ));
19491945
@@ -1997,39 +1993,36 @@ BigDecimal_DoDivmod(VALUE self, VALUE r, NULLABLE_BDVALUE *div, NULLABLE_BDVALUE
19971993 return Qtrue ;
19981994 }
19991995
2000- BigDecimal_count_precision_and_scale (self , & a_prec , NULL );
2001- BigDecimal_count_precision_and_scale (rr , & b_prec , NULL );
1996+ a_exponent = VpExponent10 (a .real );
1997+ b_exponent = VpExponent10 (b .real );
1998+ mx = a_exponent > b_exponent ? a_exponent - b_exponent + 1 : 1 ;
1999+ GUARD_OBJ (dv , NewZeroWrapLimited (1 , VPDIVD_QUO_DIGITS (mx )));
20022000
2003- mx = (a_prec > b_prec ) ? a_prec : b_prec ;
2004- mx *= 2 ;
2001+ /* res is reused for VpDivd remainder and VpMult result */
2002+ rx = VPDIVD_REM_PREC (a .real , b .real , dv .real );
2003+ mx = VPMULT_RESULT_PREC (dv .real , b .real );
2004+ GUARD_OBJ (res , NewZeroWrapNolimit (1 , Max (rx , mx ) * BASE_FIG ));
2005+ /* AddSub needs one more prec */
2006+ GUARD_OBJ (md , NewZeroWrapLimited (1 , (res .real -> MaxPrec + 1 ) * BASE_FIG ));
20052007
2006- if (2 * BIGDECIMAL_DOUBLE_FIGURES > mx )
2007- mx = 2 * BIGDECIMAL_DOUBLE_FIGURES ;
2008+ VpDivd (dv .real , res .real , a .real , b .real );
2009+ VpMidRound (dv .real , VP_ROUND_DOWN , 0 );
2010+ VpMult (res .real , dv .real , b .real );
2011+ VpAddSub (md .real , a .real , res .real , -1 );
20082012
2009- GUARD_OBJ (c , NewZeroWrapLimited (1 , mx + 2 * BASE_FIG ));
2010- GUARD_OBJ (res , NewZeroWrapNolimit (1 , mx * 2 + 2 * BASE_FIG ));
2011- VpDivd (c .real , res .real , a .real , b .real );
2012-
2013- mx = c .real -> Prec * BASE_FIG ;
2014- GUARD_OBJ (d , NewZeroWrapLimited (1 , mx ));
2015- VpActiveRound (d .real , c .real , VP_ROUND_DOWN , 0 );
2016-
2017- VpMult (res .real , d .real , b .real );
2018- VpAddSub (c .real , a .real , res .real , -1 );
2019-
2020- if (!truncate && !VpIsZero (c .real ) && (VpGetSign (a .real ) * VpGetSign (b .real ) < 0 )) {
2013+ if (!truncate && !VpIsZero (md .real ) && (VpGetSign (a .real ) * VpGetSign (b .real ) < 0 )) {
20212014 /* result adjustment for negative case */
2022- res = rbd_reallocate_struct ( res , d . real -> MaxPrec ) ;
2023- res . real -> MaxPrec = d .real -> MaxPrec ;
2024- VpAddSub ( res . real , d .real , VpOne (), -1 );
2025- GUARD_OBJ ( e , NewZeroWrapLimited ( 1 , GetAddSubPrec ( c .real , b .real ) * 2 * BASE_FIG ) );
2026- VpAddSub (e .real , c .real , b .real , 1 );
2027- * div = bdvalue_nullable (res );
2028- * mod = bdvalue_nullable (e );
2015+ BDVALUE dv2 , md2 ;
2016+ GUARD_OBJ ( dv2 , NewZeroWrapLimited ( 1 , ( dv .real -> MaxPrec + 1 ) * BASE_FIG )) ;
2017+ GUARD_OBJ ( md2 , NewZeroWrapLimited ( 1 , ( GetAddSubPrec ( md .real , b . real ) + 1 ) * BASE_FIG ) );
2018+ VpAddSub ( dv2 .real , dv .real , VpOne (), -1 );
2019+ VpAddSub (md2 .real , md .real , b .real , 1 );
2020+ * div = bdvalue_nullable (dv2 );
2021+ * mod = bdvalue_nullable (md2 );
20292022 }
20302023 else {
2031- * div = bdvalue_nullable (d );
2032- * mod = bdvalue_nullable (c );
2024+ * div = bdvalue_nullable (dv );
2025+ * mod = bdvalue_nullable (md );
20332026 }
20342027 return Qtrue ;
20352028
@@ -2121,7 +2114,7 @@ BigDecimal_div2(VALUE self, VALUE b, VALUE n)
21212114 ENTER (5 );
21222115 SIGNED_VALUE ix ;
21232116 BDVALUE av , bv , cv , res ;
2124- size_t mx , pl ;
2117+ size_t pl ;
21252118
21262119 if (NIL_P (n )) { /* div in Float sense */
21272120 NULLABLE_BDVALUE div ;
@@ -2158,11 +2151,9 @@ BigDecimal_div2(VALUE self, VALUE b, VALUE n)
21582151 ix = 2 * BIGDECIMAL_DOUBLE_FIGURES ;
21592152 }
21602153
2161- // VpDivd needs 2 extra DECDIGs for rounding.
2162- GUARD_OBJ (cv , NewZeroWrapLimited (1 , ix + 2 * VpBaseFig ()));
2163-
2164- mx = Max (av .real -> Prec , bv .real -> Prec + cv .real -> MaxPrec - 1 );
2165- GUARD_OBJ (res , NewZeroWrapNolimit (1 , mx * VpBaseFig ()));
2154+ // Needs to calculate 1 extra digit for rounding.
2155+ GUARD_OBJ (cv , NewZeroWrapLimited (1 , VPDIVD_QUO_DIGITS (ix + 1 )));
2156+ GUARD_OBJ (res , NewZeroWrapNolimit (1 , VPDIVD_REM_PREC (av .real , bv .real , cv .real ) * BASE_FIG ));
21662157 VpDivd (cv .real , res .real , av .real , bv .real );
21672158 VpSetPrecLimit (pl );
21682159 if (!VpIsZero (res .real )) {
@@ -4221,9 +4212,8 @@ BigDecimal_vpdivd(VALUE self, VALUE r, VALUE cprec) {
42214212 size_t cn = NUM2INT (cprec );
42224213 a = GetBDValueMust (self );
42234214 b = GetBDValueMust (r );
4224- size_t dn = Max (a .real -> Prec , b .real -> Prec + cn - 1 );
42254215 c = NewZeroWrapLimited (1 , cn * BASE_FIG );
4226- d = NewZeroWrapLimited (1 , dn * BASE_FIG );
4216+ d = NewZeroWrapLimited (1 , VPDIVD_REM_PREC ( a . real , b . real , c . real ) * BASE_FIG );
42274217 VpDivd (c .real , d .real , a .real , b .real );
42284218 VpNmlz (c .real );
42294219 VpNmlz (d .real );
@@ -4237,8 +4227,7 @@ BigDecimal_vpmult(VALUE self, VALUE v) {
42374227 BDVALUE a ,b ,c ;
42384228 a = GetBDValueMust (self );
42394229 b = GetBDValueMust (v );
4240- size_t cn = a .real -> Prec + b .real -> Prec + 1 ;
4241- c = NewZeroWrapLimited (1 , cn * BASE_FIG );
4230+ c = NewZeroWrapLimited (1 , VPMULT_RESULT_PREC (a .real , b .real ) * BASE_FIG );
42424231 VpMult (c .real , a .real , b .real );
42434232 VpNmlz (c .real );
42444233 RB_GC_GUARD (a .bigdecimal );
0 commit comments