Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 696d10f

Browse files
author
Stefan Krah
committed
Changes in _mpd_qexp():
----------------------- 1) Reduce the number of iterations in the Horner scheme for operands with a negative adjusted exponent. Previously the number was overestimated quite generously. 2) The function _mpd_get_exp_iterations() now has an ACL2 proof and is rewritten accordingly. 3) The proof relies on abs(op) > 9 * 10**(-prec-1), so operands without that property are now handled by the new function _mpd_qexp_check_one(). 4) The error analysis for the evaluation of the truncated Taylor series in Hull&Abrham's paper relies on the fact that the reduced operand 'r' has fewer than context.prec digits. Since the operands may have more than context.prec digits, a new ACL2 proof covers the case that r.digits > context.prec. To facilitate the proof, the Horner step now uses fma instead of rounding twice in multiply/add. Changes in mpd_qexp(): ---------------------- 1) Fix a bound in the correct rounding loop that was too optimistic. In practice results were always correctly rounded, because it is unlikely that the error in _mpd_qexp() ever reaches the theoretical maximum.
1 parent 07542a0 commit 696d10f

1 file changed

Lines changed: 117 additions & 46 deletions

File tree

Modules/_decimal/libmpdec/mpdecimal.c

Lines changed: 117 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -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
*/
38883917
static 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

Comments
 (0)