@@ -3878,53 +3878,97 @@ mpd_qdiv_u64(mpd_t *result, const mpd_t *a, uint64_t b,
38783878}
38793879#endif
38803880
3881- #if defined(_MSC_VER )
3882- /* conversion from 'double' to 'mpd_ssize_t', possible loss of data */
3883- #pragma warning(disable:4244)
3884- #endif
3881+ /* Pad the result with trailing zeros if it has fewer digits than prec. */
3882+ static void
3883+ _mpd_zeropad (mpd_t * result , const mpd_context_t * ctx , uint32_t * status )
3884+ {
3885+ if (!mpd_isspecial (result ) && !mpd_iszero (result ) &&
3886+ result -> digits < ctx -> prec ) {
3887+ mpd_ssize_t shift = ctx -> prec - result -> digits ;
3888+ mpd_qshiftl (result , result , shift , status );
3889+ result -> exp -= shift ;
3890+ }
3891+ }
3892+
3893+ /* Check if the result is guaranteed to be one. */
3894+ static int
3895+ _mpd_qexp_check_one (mpd_t * result , const mpd_t * a , const mpd_context_t * ctx ,
3896+ uint32_t * status )
3897+ {
3898+ MPD_NEW_CONST (lim ,0 ,- (ctx -> prec + 1 ),1 ,1 ,1 ,9 );
3899+ MPD_NEW_SHARED (aa , a );
3900+
3901+ mpd_set_positive (& aa );
3902+
3903+ /* abs(a) <= 9 * 10**(-prec-1) */
3904+ if (_mpd_cmp (& aa , & lim ) <= 0 ) {
3905+ _settriple (result , 0 , 1 , 0 );
3906+ _mpd_zeropad (result , ctx , status );
3907+ * status = MPD_Rounded |MPD_Inexact ;
3908+ return 1 ;
3909+ }
3910+
3911+ return 0 ;
3912+ }
3913+
38853914/*
38863915 * Get the number of iterations for the Horner scheme in _mpd_qexp().
38873916 */
38883917static inline mpd_ssize_t
3889- _mpd_get_exp_iterations (const mpd_t * a , mpd_ssize_t prec )
3918+ _mpd_get_exp_iterations (const mpd_t * r , mpd_ssize_t p )
38903919{
3891- mpd_uint_t dummy ;
3892- mpd_uint_t msdigits ;
3893- double f ;
3920+ mpd_ssize_t log10pbyr ; /* lower bound for log10(p / abs(r)) */
3921+ mpd_ssize_t n ;
38943922
3895- /* 9 is MPD_RDIGITS for 32 bit platforms */
3896- _mpd_get_msdigits ( & dummy , & msdigits , a , 9 );
3897- f = (( double ) msdigits + 1 ) / mpd_pow10 [ mpd_word_digits ( msdigits )] ;
3923+ assert ( p >= 10 );
3924+ assert (! mpd_iszero ( r ) );
3925+ assert ( - p < mpd_adjexp ( r ) && mpd_adjexp ( r ) <= -1 ) ;
38983926
38993927#ifdef CONFIG_64
3900- #ifdef USE_80BIT_LONG_DOUBLE
3901- return ceill ((1.435 * (long double )prec - 1.182 )
3902- / log10l ((long double )prec /f ));
3903- #else
3904- /* prec > floor((1ULL<<53) / 1.435) */
3905- if (prec > 6276793905742851LL ) {
3928+ if (p > (mpd_ssize_t )(1ULL <<52 )) {
39063929 return MPD_SSIZE_MAX ;
39073930 }
3908- return ceil ((1.435 * (double )prec - 1.182 ) / log10 ((double )prec /f ));
3909- #endif
3910- #else /* CONFIG_32 */
3911- return ceil ((1.435 * (double )prec - 1.182 ) / log10 ((double )prec /f ));
3912- #if defined(_MSC_VER )
3913- #pragma warning(default:4244)
3914- #endif
39153931#endif
3932+
3933+ /*
3934+ * Lower bound for log10(p / abs(r)): adjexp(p) - (adjexp(r) + 1)
3935+ * At this point (for CONFIG_64, CONFIG_32 is not problematic):
3936+ * 1) 10 <= p <= 2**52
3937+ * 2) -p < adjexp(r) <= -1
3938+ * 3) 1 <= log10pbyr <= 2**52 + 14
3939+ */
3940+ log10pbyr = (mpd_word_digits (p )- 1 ) - (mpd_adjexp (r )+ 1 );
3941+
3942+ /*
3943+ * The numerator in the paper is 1.435 * p - 1.182, calculated
3944+ * exactly. We compensate for rounding errors by using 1.43503.
3945+ * ACL2 proofs:
3946+ * 1) exp-iter-approx-lower-bound: The term below evaluated
3947+ * in 53-bit floating point arithmetic is greater than or
3948+ * equal to the exact term used in the paper.
3949+ * 2) exp-iter-approx-upper-bound: The term below is less than
3950+ * or equal to 3/2 * p <= 3/2 * 2**52.
3951+ */
3952+ n = (mpd_ssize_t )ceil ((1.43503 * (double )p - 1.182 ) / (double )log10pbyr );
3953+ return n >= 3 ? n : 3 ;
39163954}
39173955
39183956/*
3919- * Internal function, specials have been dealt with.
3957+ * Internal function, specials have been dealt with. The result has a
3958+ * relative error of less than 0.5 * 10**(-ctx->prec).
39203959 *
39213960 * The algorithm is from Hull&Abrham, Variable Precision Exponential Function,
39223961 * ACM Transactions on Mathematical Software, Vol. 12, No. 2, June 1986.
39233962 *
39243963 * Main differences:
39253964 *
3926- * - The number of iterations for the Horner scheme is calculated using the
3927- * C log10() function.
3965+ * - The number of iterations for the Horner scheme is calculated using
3966+ * 53-bit floating point arithmetic.
3967+ *
3968+ * - In the error analysis for ER (relative error accumulated in the
3969+ * evaluation of the truncated series) the reduced operand r may
3970+ * have any number of digits.
3971+ * ACL2 proof: exponent-relative-error
39283972 *
39293973 * - The analysis for early abortion has been adapted for the mpd_t
39303974 * ranges.
@@ -3941,18 +3985,23 @@ _mpd_qexp(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
39413985
39423986 assert (!mpd_isspecial (a ));
39433987
3988+ if (mpd_iszerocoeff (a )) {
3989+ _settriple (result , MPD_POS , 1 , 0 );
3990+ return ;
3991+ }
3992+
39443993 /*
3945- * We are calculating e^x = e^(r*10^t) = (e^r)^(10^t), where r < 1 and t >= 0.
3994+ * We are calculating e^x = e^(r*10^t) = (e^r)^(10^t), where abs(r) < 1 and t >= 0.
39463995 *
39473996 * If t > 0, we have:
39483997 *
3949- * (1) 0.1 <= r < 1, so e^r >= e^0.1. Overflow in the final power operation
3950- * will occur when (e^0.1)^(10^t) > 10^(emax+1). If we consider MAX_EMAX,
3951- * this will happen for t > 10 (32 bit) or (t > 19) (64 bit).
3998+ * (1) 0.1 <= r < 1, so e^0.1 <= e^r. If t > MAX_T, overflow occurs:
3999+ *
4000+ * MAX-EMAX+1 < log10(e^(0.1*10*t)) <= log10(e^(r*10^t)) < adjexp(e^(r*10^t))+1
4001+ *
4002+ * (2) -1 < r <= -0.1, so e^r <= e^-0.1. It t > MAX_T, underflow occurs:
39524003 *
3953- * (2) -1 < r <= -0.1, so e^r > e^-1. Underflow in the final power operation
3954- * will occur when (e^-1)^(10^t) < 10^(etiny-1). If we consider MIN_ETINY,
3955- * this will also happen for t > 10 (32 bit) or (t > 19) (64 bit).
4004+ * adjexp(e^(r*10^t)) <= log10(e^(r*10^t)) <= log10(e^(-0.1*10^t) < MIN-ETINY
39564005 */
39574006#if defined(CONFIG_64 )
39584007 #define MPD_EXP_MAX_T 19
@@ -3974,29 +4023,41 @@ _mpd_qexp(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
39744023 return ;
39754024 }
39764025
4026+ /* abs(a) <= 9 * 10**(-prec-1) */
4027+ if (_mpd_qexp_check_one (result , a , ctx , status )) {
4028+ return ;
4029+ }
4030+
39774031 mpd_maxcontext (& workctx );
39784032 workctx .prec = ctx -> prec + t + 2 ;
3979- workctx .prec = (workctx .prec < 9 ) ? 9 : workctx .prec ;
4033+ workctx .prec = (workctx .prec < 10 ) ? 10 : workctx .prec ;
39804034 workctx .round = MPD_ROUND_HALF_EVEN ;
39814035
3982- if ((n = _mpd_get_exp_iterations (a , workctx .prec )) == MPD_SSIZE_MAX ) {
3983- mpd_seterror (result , MPD_Invalid_operation , status ); /* GCOV_UNLIKELY */
3984- goto finish ; /* GCOV_UNLIKELY */
3985- }
3986-
39874036 if (!mpd_qcopy (result , a , status )) {
3988- goto finish ;
4037+ return ;
39894038 }
39904039 result -> exp -= t ;
39914040
4041+ /*
4042+ * At this point:
4043+ * 1) 9 * 10**(-prec-1) < abs(a)
4044+ * 2) 9 * 10**(-prec-t-1) < abs(r)
4045+ * 3) log10(9) - prec - t - 1 < log10(abs(r)) < adjexp(abs(r)) + 1
4046+ * 4) - prec - t - 2 < adjexp(abs(r)) <= -1
4047+ */
4048+ n = _mpd_get_exp_iterations (result , workctx .prec );
4049+ if (n == MPD_SSIZE_MAX ) {
4050+ mpd_seterror (result , MPD_Invalid_operation , status ); /* GCOV_UNLIKELY */
4051+ return ; /* GCOV_UNLIKELY */
4052+ }
4053+
39924054 _settriple (& sum , MPD_POS , 1 , 0 );
39934055
39944056 for (j = n - 1 ; j >= 1 ; j -- ) {
39954057 word .data [0 ] = j ;
39964058 mpd_setdigits (& word );
39974059 mpd_qdiv (& tmp , result , & word , & workctx , & workctx .status );
3998- mpd_qmul (& sum , & sum , & tmp , & workctx , & workctx .status );
3999- mpd_qadd (& sum , & sum , & one , & workctx , & workctx .status );
4060+ mpd_qfma (& sum , & sum , & tmp , & one , & workctx , & workctx .status );
40004061 }
40014062
40024063#ifdef CONFIG_64
@@ -4013,8 +4074,8 @@ _mpd_qexp(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
40134074 }
40144075#endif
40154076
4077+ _mpd_zeropad (result , ctx , status );
40164078
4017- finish :
40184079 mpd_del (& tmp );
40194080 mpd_del (& sum );
40204081 * status |= (workctx .status & MPD_Errors );
@@ -4069,8 +4130,18 @@ mpd_qexp(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
40694130 workctx .prec = prec ;
40704131 _mpd_qexp (result , a , & workctx , status );
40714132 _ssettriple (& ulp , MPD_POS , 1 ,
4072- result -> exp + result -> digits - workctx .prec - 1 );
4073-
4133+ result -> exp + result -> digits - workctx .prec );
4134+
4135+ /*
4136+ * At this point:
4137+ * 1) abs(result - e**x) < 0.5 * 10**(-prec) * e**x
4138+ * 2) result - ulp < e**x < result + ulp
4139+ * 3) result - ulp < result < result + ulp
4140+ *
4141+ * If round(result-ulp)==round(result+ulp), then
4142+ * round(result)==round(e**x). Therefore the result
4143+ * is correctly rounded.
4144+ */
40744145 workctx .prec = ctx -> prec ;
40754146 mpd_qadd (& t1 , result , & ulp , & workctx , & workctx .status );
40764147 mpd_qsub (& t2 , result , & ulp , & workctx , & workctx .status );
0 commit comments