@@ -105,8 +105,8 @@ static void _mpd_qadd(mpd_t *result, const mpd_t *a, const mpd_t *b,
105105 const mpd_context_t * ctx , uint32_t * status );
106106static inline void _mpd_qmul (mpd_t * result , const mpd_t * a , const mpd_t * b ,
107107 const mpd_context_t * ctx , uint32_t * status );
108- static void _mpd_qbarrett_divmod (mpd_t * q , mpd_t * r , const mpd_t * a ,
109- const mpd_t * b , uint32_t * status );
108+ static void _mpd_base_ndivmod (mpd_t * q , mpd_t * r , const mpd_t * a ,
109+ const mpd_t * b , uint32_t * status );
110110static inline void _mpd_qpow_uint (mpd_t * result , mpd_t * base , mpd_uint_t exp ,
111111 uint8_t resultsign , const mpd_context_t * ctx , uint32_t * status );
112112
@@ -3256,6 +3256,20 @@ mpd_qadd(mpd_t *result, const mpd_t *a, const mpd_t *b,
32563256 mpd_qfinalize (result , ctx , status );
32573257}
32583258
3259+ /* Add a and b. Set NaN/Invalid_operation if the result is inexact. */
3260+ static void
3261+ _mpd_qadd_exact (mpd_t * result , const mpd_t * a , const mpd_t * b ,
3262+ const mpd_context_t * ctx , uint32_t * status )
3263+ {
3264+ uint32_t workstatus = 0 ;
3265+
3266+ mpd_qadd (result , a , b , ctx , & workstatus );
3267+ * status |= workstatus ;
3268+ if (workstatus & (MPD_Inexact |MPD_Rounded |MPD_Clamped )) {
3269+ mpd_seterror (result , MPD_Invalid_operation , status );
3270+ }
3271+ }
3272+
32593273/* Subtract b from a. */
32603274void
32613275mpd_qsub (mpd_t * result , const mpd_t * a , const mpd_t * b ,
@@ -3273,6 +3287,20 @@ mpd_qsub(mpd_t *result, const mpd_t *a, const mpd_t *b,
32733287 mpd_qfinalize (result , ctx , status );
32743288}
32753289
3290+ /* Subtract b from a. Set NaN/Invalid_operation if the result is inexact. */
3291+ static void
3292+ _mpd_qsub_exact (mpd_t * result , const mpd_t * a , const mpd_t * b ,
3293+ const mpd_context_t * ctx , uint32_t * status )
3294+ {
3295+ uint32_t workstatus = 0 ;
3296+
3297+ mpd_qsub (result , a , b , ctx , & workstatus );
3298+ * status |= workstatus ;
3299+ if (workstatus & (MPD_Inexact |MPD_Rounded |MPD_Clamped )) {
3300+ mpd_seterror (result , MPD_Invalid_operation , status );
3301+ }
3302+ }
3303+
32763304/* Add decimal and mpd_ssize_t. */
32773305void
32783306mpd_qadd_ssize (mpd_t * result , const mpd_t * a , mpd_ssize_t b ,
@@ -3500,7 +3528,7 @@ _mpd_qdiv(int action, mpd_t *q, const mpd_t *a, const mpd_t *b,
35003528 }
35013529 else {
35023530 MPD_NEW_STATIC (r ,0 ,0 ,0 ,0 );
3503- _mpd_qbarrett_divmod (q , & r , a , b , status );
3531+ _mpd_base_ndivmod (q , & r , a , b , status );
35043532 if (mpd_isspecial (q ) || mpd_isspecial (& r )) {
35053533 mpd_del (& r );
35063534 goto finish ;
@@ -3652,14 +3680,10 @@ _mpd_qdivmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b,
36523680 }
36533681 }
36543682 else {
3655- _mpd_qbarrett_divmod (q , r , a , b , status );
3683+ _mpd_base_ndivmod (q , r , a , b , status );
36563684 if (mpd_isspecial (q ) || mpd_isspecial (r )) {
36573685 goto nanresult ;
36583686 }
3659- if (mpd_isinfinite (q ) || q -> digits > ctx -> prec ) {
3660- * status |= MPD_Division_impossible ;
3661- goto nanresult ;
3662- }
36633687 qsize = q -> len ;
36643688 rsize = r -> len ;
36653689 }
@@ -5369,6 +5393,20 @@ mpd_qmul(mpd_t *result, const mpd_t *a, const mpd_t *b,
53695393 mpd_qfinalize (result , ctx , status );
53705394}
53715395
5396+ /* Multiply a and b. Set NaN/Invalid_operation if the result is inexact. */
5397+ static void
5398+ _mpd_qmul_exact (mpd_t * result , const mpd_t * a , const mpd_t * b ,
5399+ const mpd_context_t * ctx , uint32_t * status )
5400+ {
5401+ uint32_t workstatus = 0 ;
5402+
5403+ mpd_qmul (result , a , b , ctx , & workstatus );
5404+ * status |= workstatus ;
5405+ if (workstatus & (MPD_Inexact |MPD_Rounded |MPD_Clamped )) {
5406+ mpd_seterror (result , MPD_Invalid_operation , status );
5407+ }
5408+ }
5409+
53725410/* Multiply decimal and mpd_ssize_t. */
53735411void
53745412mpd_qmul_ssize (mpd_t * result , const mpd_t * a , mpd_ssize_t b ,
@@ -6691,11 +6729,18 @@ recpr_schedule_prec(mpd_ssize_t klist[MPD_MAX_PREC_LOG2],
66916729 return i - 1 ;
66926730}
66936731
6694- /* Initial approximation for the reciprocal. */
6732+ /*
6733+ * Initial approximation for the reciprocal:
6734+ * k_0 := MPD_RDIGITS-2
6735+ * z_0 := 10**(-k_0) * floor(10**(2*k_0 + 2) / floor(v * 10**(k_0 + 2)))
6736+ * Absolute error:
6737+ * |1/v - z_0| < 10**(-k_0)
6738+ * ACL2 proof: maxerror-inverse-approx
6739+ */
66956740static void
66966741_mpd_qreciprocal_approx (mpd_t * z , const mpd_t * v , uint32_t * status )
66976742{
6698- mpd_uint_t p10data [2 ] = {0 , mpd_pow10 [MPD_RDIGITS - 2 ]}; /* 10**(2*MPD_RDIGITS-2) */
6743+ mpd_uint_t p10data [2 ] = {0 , mpd_pow10 [MPD_RDIGITS - 2 ]};
66996744 mpd_uint_t dummy , word ;
67006745 int n ;
67016746
@@ -6714,7 +6759,12 @@ _mpd_qreciprocal_approx(mpd_t *z, const mpd_t *v, uint32_t *status)
67146759 mpd_setdigits (z );
67156760}
67166761
6717- /* Reciprocal, calculated with Newton's Method. Assumption: result != a. */
6762+ /*
6763+ * Reciprocal, calculated with Newton's Method. Assumption: result != a.
6764+ * NOTE: The comments in the function show that certain operations are
6765+ * exact. The proof for the maximum error is too long to fit in here.
6766+ * ACL2 proof: maxerror-inverse-complete
6767+ */
67186768static void
67196769_mpd_qreciprocal (mpd_t * result , const mpd_t * a , const mpd_context_t * ctx ,
67206770 uint32_t * status )
@@ -6738,32 +6788,43 @@ _mpd_qreciprocal(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
67386788 adj = v -> digits + v -> exp ;
67396789 v -> exp = - v -> digits ;
67406790
6741- /* initial approximation */
6791+ /* Initial approximation */
67426792 _mpd_qreciprocal_approx (z , v , status );
67436793
67446794 mpd_maxcontext (& varcontext );
67456795 mpd_maxcontext (& maxcontext );
6746- varcontext .round = MPD_ROUND_TRUNC ;
6747- maxcontext .round = MPD_ROUND_TRUNC ;
6796+ varcontext .round = maxcontext .round = MPD_ROUND_TRUNC ;
6797+ varcontext .emax = maxcontext .emax = MPD_MAX_EMAX + 100 ;
6798+ varcontext .emin = maxcontext .emin = MPD_MIN_EMIN - 100 ;
6799+ maxcontext .prec = MPD_MAX_PREC + 100 ;
67486800
6749- maxprec = ( v -> digits > ctx -> prec ) ? v -> digits : ctx -> prec ;
6801+ maxprec = ctx -> prec ;
67506802 maxprec += 2 ;
67516803 initprec = MPD_RDIGITS - 3 ;
67526804
67536805 i = recpr_schedule_prec (klist , maxprec , initprec );
67546806 for (; i >= 0 ; i -- ) {
6755- mpd_qmul (& s , z , z , & maxcontext , status );
6807+ /* Loop invariant: z->digits <= klist[i]+7 */
6808+ /* Let s := z**2, exact result */
6809+ _mpd_qmul_exact (& s , z , z , & maxcontext , status );
67566810 varcontext .prec = 2 * klist [i ] + 5 ;
67576811 if (v -> digits > varcontext .prec ) {
6812+ /* Let t := v, truncated to n >= 2*k+5 fraction digits */
67586813 mpd_qshiftr (& t , v , v -> digits - varcontext .prec , status );
67596814 t .exp = - varcontext .prec ;
6815+ /* Let t := trunc(v)*s, truncated to n >= 2*k+1 fraction digits */
67606816 mpd_qmul (& t , & t , & s , & varcontext , status );
67616817 }
6762- else {
6818+ else { /* v->digits <= 2*k+5 */
6819+ /* Let t := v*s, truncated to n >= 2*k+1 fraction digits */
67636820 mpd_qmul (& t , v , & s , & varcontext , status );
67646821 }
6765- mpd_qmul (& s , z , & two , & maxcontext , status );
6766- mpd_qsub (z , & s , & t , & maxcontext , status );
6822+ /* Let s := 2*z, exact result */
6823+ _mpd_qmul_exact (& s , z , & two , & maxcontext , status );
6824+ /* s.digits < t.digits <= 2*k+5, |adjexp(s)-adjexp(t)| <= 1,
6825+ * so the subtraction generates at most 2*k+6 <= klist[i+1]+7
6826+ * digits. The loop invariant is preserved. */
6827+ _mpd_qsub_exact (z , & s , & t , & maxcontext , status );
67676828 }
67686829
67696830 if (!mpd_isspecial (z )) {
@@ -6777,22 +6838,29 @@ _mpd_qreciprocal(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
67776838}
67786839
67796840/*
6780- * Integer division with remainder of the coefficients: coeff(a) / coeff(b).
6781- * This function is for large numbers where it is faster to divide by
6782- * multiplying the dividend by the reciprocal of the divisor.
6783- * The inexact result is fixed by a small loop, which should not take
6784- * more than 2 iterations.
6841+ * Internal function for large numbers:
6842+ *
6843+ * q, r = divmod(coeff(a), coeff(b))
6844+ *
6845+ * Strategy: Multiply the dividend by the reciprocal of the divisor. The
6846+ * inexact result is fixed by a small loop, using at most 2 iterations.
6847+ *
6848+ * ACL2 proofs:
6849+ * ------------
6850+ * 1) q is a natural number. (ndivmod-quotient-natp)
6851+ * 2) r is a natural number. (ndivmod-remainder-natp)
6852+ * 3) a = q * b + r (ndivmod-q*b+r==a)
6853+ * 4) r < b (ndivmod-remainder-<-b)
67856854 */
67866855static void
6787- _mpd_qbarrett_divmod (mpd_t * q , mpd_t * r , const mpd_t * a , const mpd_t * b ,
6788- uint32_t * status )
6856+ _mpd_base_ndivmod (mpd_t * q , mpd_t * r , const mpd_t * a , const mpd_t * b ,
6857+ uint32_t * status )
67896858{
67906859 mpd_context_t workctx ;
67916860 mpd_t * qq = q , * rr = r ;
67926861 mpd_t aa , bb ;
67936862 int k ;
67946863
6795- mpd_maxcontext (& workctx );
67966864 _mpd_copy_shared (& aa , a );
67976865 _mpd_copy_shared (& bb , b );
67986866
@@ -6814,41 +6882,68 @@ _mpd_qbarrett_divmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b,
68146882 }
68156883 }
68166884
6817- /* maximum length of q + 3 digits */
6818- workctx .prec = aa .digits - bb .digits + 1 + 3 ;
6819- /* we get the reciprocal with precision maxlen(q) + 3 */
6885+ mpd_maxcontext (& workctx );
6886+
6887+ /* Let prec := adigits - bdigits + 4 */
6888+ workctx .prec = a -> digits - b -> digits + 1 + 3 ;
6889+ if (a -> digits > MPD_MAX_PREC || workctx .prec > MPD_MAX_PREC ) {
6890+ * status |= MPD_Division_impossible ;
6891+ goto nanresult ;
6892+ }
6893+
6894+ /* Let x := _mpd_qreciprocal(b, prec)
6895+ * Then x is bounded by:
6896+ * 1) 1/b - 10**(-prec - bdigits) < x < 1/b + 10**(-prec - bdigits)
6897+ * 2) 1/b - 10**(-adigits - 4) < x < 1/b + 10**(-adigits - 4)
6898+ */
68206899 _mpd_qreciprocal (rr , & bb , & workctx , & workctx .status );
68216900
6822- mpd_qmul (qq , & aa , rr , & workctx , & workctx .status );
6901+ /* Get an estimate for the quotient. Let q := a * x
6902+ * Then q is bounded by:
6903+ * 3) a/b - 10**-4 < q < a/b + 10**-4
6904+ */
6905+ _mpd_qmul (qq , & aa , rr , & workctx , & workctx .status );
6906+ /* Truncate q to an integer:
6907+ * 4) a/b - 2 < trunc(q) < a/b + 1
6908+ */
68236909 mpd_qtrunc (qq , qq , & workctx , & workctx .status );
68246910
68256911 workctx .prec = aa .digits + 3 ;
6826- /* get the remainder */
6827- mpd_qmul (rr , & bb , qq , & workctx , & workctx .status );
6828- mpd_qsub (rr , & aa , rr , & workctx , & workctx .status );
6912+ workctx .emax = MPD_MAX_EMAX + 3 ;
6913+ workctx .emin = MPD_MIN_EMIN - 3 ;
6914+ /* Multiply the estimate for q by b:
6915+ * 5) a - 2 * b < trunc(q) * b < a + b
6916+ */
6917+ _mpd_qmul (rr , & bb , qq , & workctx , & workctx .status );
6918+ /* Get the estimate for r such that a = q * b + r. */
6919+ _mpd_qsub_exact (rr , & aa , rr , & workctx , & workctx .status );
68296920
6830- /* Fix the result. Algorithm from: Karl Hasselstrom, Fast Division of Large Integers */
6921+ /* Fix the result. At this point -b < r < 2*b, so the correction loop
6922+ takes at most one iteration. */
68316923 for (k = 0 ;; k ++ ) {
6832- if (mpd_isspecial (rr )) {
6924+ if (mpd_isspecial (qq ) || mpd_isspecial ( rr )) {
68336925 * status |= (workctx .status & MPD_Errors );
68346926 goto nanresult ;
68356927 }
6836- if (k > 2 ) {
6837- mpd_err_warn ("libmpdec: internal error in " /* GCOV_NOT_REACHED */
6838- "_mpd_qbarrett_divmod : please report" ); /* GCOV_NOT_REACHED */
6839- * status |= MPD_Invalid_operation ; /* GCOV_NOT_REACHED */
6840- goto nanresult ; /* GCOV_NOT_REACHED */
6928+ if (k > 2 ) { /* Allow two iterations despite the proof. */
6929+ mpd_err_warn ("libmpdec: internal error in " /* GCOV_NOT_REACHED */
6930+ "_mpd_base_ndivmod : please report" ); /* GCOV_NOT_REACHED */
6931+ * status |= MPD_Invalid_operation ; /* GCOV_NOT_REACHED */
6932+ goto nanresult ; /* GCOV_NOT_REACHED */
68416933 }
6934+ /* r < 0 */
68426935 else if (_mpd_cmp (& zero , rr ) == 1 ) {
6843- mpd_qadd (rr , rr , & bb , & workctx , & workctx .status );
6844- mpd_qadd (qq , qq , & minus_one , & workctx , & workctx .status );
6936+ _mpd_qadd_exact (rr , rr , & bb , & workctx , & workctx .status );
6937+ _mpd_qadd_exact (qq , qq , & minus_one , & workctx , & workctx .status );
68456938 }
6939+ /* 0 <= r < b */
68466940 else if (_mpd_cmp (rr , & bb ) == -1 ) {
68476941 break ;
68486942 }
6943+ /* r >= b */
68496944 else {
6850- mpd_qsub (rr , rr , & bb , & workctx , & workctx .status );
6851- mpd_qadd (qq , qq , & one , & workctx , & workctx .status );
6945+ _mpd_qsub_exact (rr , rr , & bb , & workctx , & workctx .status );
6946+ _mpd_qadd_exact (qq , qq , & one , & workctx , & workctx .status );
68526947 }
68536948 }
68546949
0 commit comments