@@ -174,6 +174,14 @@ check_allocation_count_nonzero(void)
174
174
# define check_allocation_count_nonzero () /* nothing */
175
175
#endif /* BIGDECIMAL_DEBUG */
176
176
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
+
177
185
static NULLABLE_BDVALUE
178
186
CreateFromString (size_t mx , const char * str , VALUE klass , bool strict_p , bool raise_exception );
179
187
@@ -222,16 +230,6 @@ rbd_allocate_struct_decimal_digits(size_t const decimal_digits, bool limit_preci
222
230
223
231
static VALUE BigDecimal_wrap_struct (VALUE obj , Real * vp );
224
232
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
-
235
233
static void
236
234
rbd_free_struct (Real * real )
237
235
{
@@ -1843,7 +1841,6 @@ BigDecimal_mult(VALUE self, VALUE r)
1843
1841
{
1844
1842
ENTER (5 );
1845
1843
BDVALUE a , b , c ;
1846
- size_t mx ;
1847
1844
1848
1845
GUARD_OBJ (a , GetBDValueMust (self ));
1849
1846
if (RB_TYPE_P (r , T_FLOAT )) {
@@ -1859,8 +1856,7 @@ BigDecimal_mult(VALUE self, VALUE r)
1859
1856
b = bdvalue_nonnullable (b2 );
1860
1857
}
1861
1858
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 ));
1864
1860
VpMult (c .real , a .real , b .real );
1865
1861
return CheckGetValue (c );
1866
1862
}
@@ -1941,9 +1937,9 @@ static VALUE
1941
1937
BigDecimal_DoDivmod (VALUE self , VALUE r , NULLABLE_BDVALUE * div , NULLABLE_BDVALUE * mod , bool truncate )
1942
1938
{
1943
1939
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 ;
1947
1943
1948
1944
GUARD_OBJ (a , GetBDValueMust (self ));
1949
1945
@@ -1997,39 +1993,36 @@ BigDecimal_DoDivmod(VALUE self, VALUE r, NULLABLE_BDVALUE *div, NULLABLE_BDVALUE
1997
1993
return Qtrue ;
1998
1994
}
1999
1995
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 )));
2002
2000
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 ));
2005
2007
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 );
2008
2012
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 )) {
2021
2014
/* 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 );
2029
2022
}
2030
2023
else {
2031
- * div = bdvalue_nullable (d );
2032
- * mod = bdvalue_nullable (c );
2024
+ * div = bdvalue_nullable (dv );
2025
+ * mod = bdvalue_nullable (md );
2033
2026
}
2034
2027
return Qtrue ;
2035
2028
@@ -2121,7 +2114,7 @@ BigDecimal_div2(VALUE self, VALUE b, VALUE n)
2121
2114
ENTER (5 );
2122
2115
SIGNED_VALUE ix ;
2123
2116
BDVALUE av , bv , cv , res ;
2124
- size_t mx , pl ;
2117
+ size_t pl ;
2125
2118
2126
2119
if (NIL_P (n )) { /* div in Float sense */
2127
2120
NULLABLE_BDVALUE div ;
@@ -2158,11 +2151,9 @@ BigDecimal_div2(VALUE self, VALUE b, VALUE n)
2158
2151
ix = 2 * BIGDECIMAL_DOUBLE_FIGURES ;
2159
2152
}
2160
2153
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 ));
2166
2157
VpDivd (cv .real , res .real , av .real , bv .real );
2167
2158
VpSetPrecLimit (pl );
2168
2159
if (!VpIsZero (res .real )) {
@@ -4221,9 +4212,8 @@ BigDecimal_vpdivd(VALUE self, VALUE r, VALUE cprec) {
4221
4212
size_t cn = NUM2INT (cprec );
4222
4213
a = GetBDValueMust (self );
4223
4214
b = GetBDValueMust (r );
4224
- size_t dn = Max (a .real -> Prec , b .real -> Prec + cn - 1 );
4225
4215
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 );
4227
4217
VpDivd (c .real , d .real , a .real , b .real );
4228
4218
VpNmlz (c .real );
4229
4219
VpNmlz (d .real );
@@ -4237,8 +4227,7 @@ BigDecimal_vpmult(VALUE self, VALUE v) {
4237
4227
BDVALUE a ,b ,c ;
4238
4228
a = GetBDValueMust (self );
4239
4229
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 );
4242
4231
VpMult (c .real , a .real , b .real );
4243
4232
VpNmlz (c .real );
4244
4233
RB_GC_GUARD (a .bigdecimal );
0 commit comments