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

Skip to content

Commit a3394bc

Browse files
author
Stefan Krah
committed
1) Add error analysis comments to mpd_qln10() and _mpd_qln().
2) Simplify the precision adjustment code for values in [0.900, 1.15].
1 parent a01f1ad commit a3394bc

1 file changed

Lines changed: 96 additions & 31 deletions

File tree

Modules/_decimal/libmpdec/mpdecimal.c

Lines changed: 96 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4212,6 +4212,18 @@ mpd_qfma(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_t *c,
42124212
*status |= workstatus;
42134213
}
42144214

4215+
/*
4216+
* Schedule the optimal precision increase for the Newton iteration.
4217+
* v := input operand
4218+
* z_0 := initial approximation
4219+
* initprec := natural number such that abs(log(v) - z_0) < 10**-initprec
4220+
* maxprec := target precision
4221+
*
4222+
* For convenience the output klist contains the elements in reverse order:
4223+
* klist := [k_n-1, ..., k_0], where
4224+
* 1) k_0 <= initprec and
4225+
* 2) abs(log(v) - result) < 10**(-2*k_n-1 + 1) <= 10**-maxprec.
4226+
*/
42154227
static inline int
42164228
ln_schedule_prec(mpd_ssize_t klist[MPD_MAX_PREC_LOG2], mpd_ssize_t maxprec,
42174229
mpd_ssize_t initprec)
@@ -4231,6 +4243,7 @@ ln_schedule_prec(mpd_ssize_t klist[MPD_MAX_PREC_LOG2], mpd_ssize_t maxprec,
42314243
return i-1;
42324244
}
42334245

4246+
/* The constants have been verified with both decimal.py and mpfr. */
42344247
#ifdef CONFIG_64
42354248
#if MPD_RDIGITS != 19
42364249
#error "mpdecimal.c: MPD_RDIGITS must be 19."
@@ -4285,7 +4298,7 @@ static const mpd_t _mpd_ln10 = {
42854298
(mpd_uint_t *)mpd_ln10_data
42864299
};
42874300

4288-
/* Set 'result' to ln(10), with 'prec' digits, using ROUND_HALF_EVEN. */
4301+
/* Set 'result' to ln(10). ulp error: abs(result - log(10)) < ulp(log(10)) */
42894302
void
42904303
mpd_qln10(mpd_t *result, mpd_ssize_t prec, uint32_t *status)
42914304
{
@@ -4320,7 +4333,7 @@ mpd_qln10(mpd_t *result, mpd_ssize_t prec, uint32_t *status)
43204333
mpd_maxcontext(&varcontext);
43214334
varcontext.round = MPD_ROUND_TRUNC;
43224335

4323-
i = ln_schedule_prec(klist, prec+2, result->digits);
4336+
i = ln_schedule_prec(klist, prec+2, -result->exp);
43244337
for (; i >= 0; i--) {
43254338
varcontext.prec = 2*klist[i]+3;
43264339
result->flags ^= MPD_NEG;
@@ -4339,7 +4352,18 @@ mpd_qln10(mpd_t *result, mpd_ssize_t prec, uint32_t *status)
43394352
mpd_qfinalize(result, &maxcontext, status);
43404353
}
43414354

4342-
/* Initial approximations for the ln() iteration */
4355+
/*
4356+
* Initial approximations for the ln() iteration. The values have the
4357+
* following properties (established with both decimal.py and mpfr):
4358+
*
4359+
* Index 0 - 400, logarithms of x in [1.00, 5.00]:
4360+
* abs(lnapprox[i] * 10**-3 - log((i+100)/100)) < 10**-2
4361+
* abs(lnapprox[i] * 10**-3 - log((i+1+100)/100)) < 10**-2
4362+
*
4363+
* Index 401 - 899, logarithms of x in (0.500, 0.999]:
4364+
* abs(-lnapprox[i] * 10**-3 - log((i+100)/1000)) < 10**-2
4365+
* abs(-lnapprox[i] * 10**-3 - log((i+1+100)/1000)) < 10**-2
4366+
*/
43434367
static const uint16_t lnapprox[900] = {
43444368
/* index 0 - 400: log((i+100)/100) * 1000 */
43454369
0, 10, 20, 30, 39, 49, 58, 68, 77, 86, 95, 104, 113, 122, 131, 140, 148, 157,
@@ -4406,7 +4430,10 @@ static const uint16_t lnapprox[900] = {
44064430
18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1
44074431
};
44084432

4409-
/* Internal ln() function that does not check for specials, zero or one. */
4433+
/*
4434+
* Internal ln() function that does not check for specials, zero or one.
4435+
* Relative error: abs(result - log(a)) < 0.1 * 10**-prec * abs(log(a))
4436+
*/
44104437
static void
44114438
_mpd_qln(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
44124439
uint32_t *status)
@@ -4451,10 +4478,16 @@ _mpd_qln(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
44514478
mpd_setdigits(z);
44524479

44534480
if (x <= 400) {
4481+
/* Reduce the input operand to 1.00 <= v <= 5.00. Let y = x + 100,
4482+
* so 100 <= y <= 500. Since y contains the most significant digits
4483+
* of v, y/100 <= v < (y+1)/100 and abs(z - log(v)) < 10**-2. */
44544484
v.exp = -(a_digits - 1);
44554485
t = a_exp + a_digits - 1;
44564486
}
44574487
else {
4488+
/* Reduce the input operand to 0.500 < v <= 0.999. Let y = x + 100,
4489+
* so 500 < y <= 999. Since y contains the most significant digits
4490+
* of v, y/1000 <= v < (y+1)/1000 and abs(z - log(v)) < 10**-2. */
44584491
v.exp = -a_digits;
44594492
t = a_exp + a_digits;
44604493
mpd_set_negative(z);
@@ -4465,37 +4498,46 @@ _mpd_qln(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
44654498
varcontext.round = MPD_ROUND_TRUNC;
44664499

44674500
maxprec = ctx->prec + 2;
4468-
if (x <= 10 || x >= 805) {
4469-
/* v is close to 1: Estimate the magnitude of the logarithm.
4470-
* If v = 1 or ln(v) will underflow, skip the loop. Otherwise,
4471-
* adjust the precision upwards in order to obtain a sufficient
4472-
* number of significant digits.
4473-
*
4474-
* 1) x/(1+x) < ln(1+x) < x, for x > -1, x != 0
4501+
if (t == 0 && (x <= 15 || x >= 800)) {
4502+
/* 0.900 <= v <= 1.15: Estimate the magnitude of the logarithm.
4503+
* If ln(v) will underflow, skip the loop. Otherwise, adjust the
4504+
* precision upwards in order to obtain a sufficient number of
4505+
* significant digits.
44754506
*
4476-
* 2) (v-1)/v < ln(v) < v-1
4507+
* Case v > 1:
4508+
* abs((v-1)/10) < abs((v-1)/v) < abs(ln(v)) < abs(v-1)
4509+
* Case v < 1:
4510+
* abs(v-1) < abs(ln(v)) < abs((v-1)/v) < abs((v-1)*10)
44774511
*/
4478-
mpd_t *lower = &tmp;
4479-
mpd_t *upper = &vtmp;
44804512
int cmp = _mpd_cmp(&v, &one);
44814513

4482-
varcontext.round = MPD_ROUND_CEILING;
4483-
varcontext.prec = maxprec;
4484-
mpd_qsub(upper, &v, &one, &varcontext, &varcontext.status);
4485-
varcontext.round = MPD_ROUND_FLOOR;
4486-
mpd_qdiv(lower, upper, &v, &varcontext, &varcontext.status);
4487-
varcontext.round = MPD_ROUND_TRUNC;
4514+
/* Upper bound (assume v > 1): abs(v-1), unrounded */
4515+
_mpd_qsub(&tmp, &v, &one, &maxcontext, &maxcontext.status);
4516+
if (maxcontext.status & MPD_Errors) {
4517+
mpd_seterror(result, MPD_Malloc_error, status);
4518+
goto finish;
4519+
}
44884520

44894521
if (cmp < 0) {
4490-
_mpd_ptrswap(&upper, &lower);
4522+
/* v < 1: abs((v-1)*10) */
4523+
tmp.exp += 1;
44914524
}
4492-
if (mpd_adjexp(upper) < mpd_etiny(ctx)) {
4493-
_settriple(z, (cmp<0), 1, mpd_etiny(ctx)-1);
4494-
goto postloop;
4525+
if (mpd_adjexp(&tmp) < mpd_etiny(ctx)) {
4526+
/* The upper bound is less than etiny: Underflow to zero */
4527+
_settriple(result, (cmp<0), 1, mpd_etiny(ctx)-1);
4528+
goto finish;
44954529
}
4496-
/* XXX optimization: t == 0 && mpd_adjexp(lower) < 0 */
4497-
if (mpd_adjexp(lower) < 0) {
4498-
maxprec = maxprec - mpd_adjexp(lower);
4530+
/* Lower bound: abs((v-1)/10) or abs(v-1) */
4531+
tmp.exp -= 1;
4532+
if (mpd_adjexp(&tmp) < 0) {
4533+
/* Absolute error of the loop: abs(z - log(v)) < 10**-p. If
4534+
* p = ctx->prec+2-adjexp(lower), then the relative error of
4535+
* the result is (using 10**adjexp(x) <= abs(x)):
4536+
*
4537+
* abs(z - log(v)) / abs(log(v)) < 10**-p / abs(log(v))
4538+
* <= 10**(-ctx->prec-2)
4539+
*/
4540+
maxprec = maxprec - mpd_adjexp(&tmp);
44994541
}
45004542
}
45014543

@@ -4523,14 +4565,37 @@ _mpd_qln(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
45234565
}
45244566
}
45254567

4526-
postloop:
4527-
mpd_qln10(&v, maxprec+2, status);
4568+
/*
4569+
* Case t == 0:
4570+
* t * log(10) == 0, the result does not change and the analysis
4571+
* above applies. If v < 0.900 or v > 1.15, the relative error is
4572+
* less than 10**(-ctx.prec-1).
4573+
* Case t != 0:
4574+
* z := approx(log(v))
4575+
* y := approx(log(10))
4576+
* p := maxprec = ctx->prec + 2
4577+
* Absolute errors:
4578+
* 1) abs(z - log(v)) < 10**-p
4579+
* 2) abs(y - log(10)) < 10**-p
4580+
* The multiplication is exact, so:
4581+
* 3) abs(t*y - t*log(10)) < t*10**-p
4582+
* The sum is exact, so:
4583+
* 4) abs((z + t*y) - (log(v) + t*log(10))) < (abs(t) + 1) * 10**-p
4584+
* Bounds for log(v) and log(10):
4585+
* 5) -7/10 < log(v) < 17/10
4586+
* 6) 23/10 < log(10) < 24/10
4587+
* Using 4), 5), 6) and t != 0, the relative error is:
4588+
*
4589+
* 7) relerr < ((abs(t) + 1)*10**-p) / abs(log(v) + t*log(10))
4590+
* < 0.5 * 10**(-p + 1) = 0.5 * 10**(-ctx->prec-1)
4591+
*/
4592+
mpd_qln10(&v, maxprec+1, status);
45284593
mpd_qmul_ssize(&tmp, &v, t, &maxcontext, status);
4529-
varcontext.prec = maxprec+2;
4530-
mpd_qadd(result, &tmp, z, &varcontext, status);
4594+
mpd_qadd(result, &tmp, z, &maxcontext, status);
45314595

45324596

45334597
finish:
4598+
*status |= (MPD_Inexact|MPD_Rounded);
45344599
mpd_del(&v);
45354600
mpd_del(&vtmp);
45364601
mpd_del(&tmp);

0 commit comments

Comments
 (0)