diff options
author | zimmerma <zimmerma@280ebfd0-de03-0410-8827-d642c229c3f4> | 2017-12-18 08:51:06 +0000 |
---|---|---|
committer | zimmerma <zimmerma@280ebfd0-de03-0410-8827-d642c229c3f4> | 2017-12-18 08:51:06 +0000 |
commit | 974825f8f3023a6d9e5c75d6010000064dc2672e (patch) | |
tree | 9b9989f9c9df406eddcf41fd04fa3fffb6ab97ed | |
parent | af989821960f0e6d15d015480406301174b6dd03 (diff) | |
download | mpfr-974825f8f3023a6d9e5c75d6010000064dc2672e.tar.gz |
[tests/ttanh.c] added test for bug in mpfr_tanh
[doc/algorithms.tex] fixed error analysis for mpfr_tanh
[src/tanh.c] fixed error analysis
Note after r12016: Even though mpfr_tanh was incorrect, the failure
of the test added in ttanh.c was actually *only* due to a bug in the
mpfr_div code specific to the trunk (fixed in r12002), i.e. this was
not a non-regression test for the mpfr_tanh bug itself (in particular,
this test does not introduce a failure in the 3.1 branch, which still
has the same incorrect mpfr_tanh code but a correct mpfr_div).
git-svn-id: svn://scm.gforge.inria.fr/svn/mpfr/trunk@11993 280ebfd0-de03-0410-8827-d642c229c3f4
-rw-r--r-- | doc/algorithms.tex | 25 | ||||
-rw-r--r-- | src/tanh.c | 19 | ||||
-rw-r--r-- | tests/ttanh.c | 20 |
3 files changed, 47 insertions, 17 deletions
diff --git a/doc/algorithms.tex b/doc/algorithms.tex index d5311d1c3..bc1a1eaeb 100644 --- a/doc/algorithms.tex +++ b/doc/algorithms.tex @@ -1854,7 +1854,8 @@ that $x \geq 0$. We use Higham's notation, with $\theta_i$ denoting variables such that $|\theta_i| \leq 2^{-p}$. -Firstly, $u$ is exact. Then $v = e^{2x} (1+\theta_1)$ and +Firstly, $u$ is exact, assuming $x$ is exact with precision $p$. +Then $v = e^{2x} (1+\theta_1)$ and $w = (e^{2x}+1) (1+\theta_2)^2$. The error on $r$ is bounded by $\frac{1}{2} \ulp(v) + \frac{1}{2} \ulp(r)$. Assume $\ulp(v) = 2^e \ulp(r)$, with $e \geq 0$; @@ -1864,27 +1865,29 @@ and then $s = \tanh(x) (1+\theta_4)^{2^e+4}$. \begin{lemma} For $|x| \leq 1/2$, and $|y| \leq |x|^{-1/2}$, we have: -\[ |(1+x)^y-1| \leq 2 |y| x. \] +\[ |(1+x)^y-1| \leq 2.5 |y| x. \] \end{lemma} \begin{proof} We have $(1+x)^y = e^{y \log (1+x)}$, with $|y \log (1+x)| \leq |x|^{-1/2} |\log (1+x)|$. The function $|x|^{-1/2} \log (1+x)$ is increasing on $[-1/2,1/2]$, and -takes as values $\approx -0.490$ in $x=-1/2$ and $\approx 0.286$ in $x=1/2$, -thus is bounded in absolute value by $1/2$. -This yields $|y \log (1+x)| \leq 1/2$. -Now it is easy to see that for $|t| \leq 1/2$, we have -$|e^t-1| \leq 1.3 |t|$. -Thus $|(1+x)^y-1| \leq 1.3 |y| |\log (1+x)|$. +takes as values $\approx -0.980$ in $x=-1/2$ and $\approx 0.573$ in $x=1/2$, +thus is bounded in absolute value by $1$. +This yields $|y \log (1+x)| \leq 1$. +Now it is easy to see that for $|t| \leq 1$, we have +$|e^t-1| \leq 1.72 |t|$. +Thus $|(1+x)^y-1| \leq 1.72 |y| |\log (1+x)|$. The result follows from $|\log (1+x)| \leq 1.4 |x|$ for $|x| \leq 1/2$, -and $1.3 \times 1.4 \leq 2$. +and $1.72 \times 1.4 \leq 2.5$. \end{proof} Applying the above lemma for $x=\theta_4$ and $y=2^e+4$, assuming $2^e+4 \leq 2^{p/2}$, -we get $s = \tanh(x) [1 + 2(2^e+4)\theta_5]$. +we get $|(1+\theta_4)^{2^e+4} - 1| \leq 2.5 (2^e+4) |\theta_4|$, and thus +we can write $s = \tanh(x) [1 + 2.5 (2^e+4)\theta_5]$ with +$|\theta_5| \leq 2^{-p}$. Since $2^e+4 \leq 2^{{\rm max}(3,e+1)}$, -the relative error on $s$ is thus bounded by $2^{{\rm max}(4,e+2)-p}$. +the relative error on $s$ is thus bounded by $2^{{\rm max}(5,e+3)-p}$. \subsection{The inverse hyperbolic tangent function} diff --git a/src/tanh.c b/src/tanh.c index 06adf7874..e731f2623 100644 --- a/src/tanh.c +++ b/src/tanh.c @@ -98,13 +98,20 @@ mpfr_tanh (mpfr_ptr y, mpfr_srcptr xt , mpfr_rnd_t rnd_mode) if (MPFR_GET_EXP (x) < 0) Nt += -MPFR_GET_EXP (x); + /* The error analysis in algorithms.tex assumes that x is exactly + representable with working precision Nt. + FIXME: adapt the error analysis for the case Nt < PREC(x). */ + if (Nt < MPFR_PREC(x)) + Nt = MPFR_PREC(x); + /* initialize of intermediary variable */ MPFR_GROUP_INIT_2 (group, Nt, t, te); MPFR_ZIV_INIT (loop, Nt); for (;;) { /* tanh = (exp(2x)-1)/(exp(2x)+1) */ - mpfr_mul_2ui (te, x, 1, MPFR_RNDN); /* 2x */ + inexact = mpfr_mul_2ui (te, x, 1, MPFR_RNDN); /* 2x */ + MPFR_ASSERTD(inexact == 0); /* see FIXME above */ /* since x > 0, we can only have an overflow */ mpfr_exp (te, te, MPFR_RNDN); /* exp(2x) */ if (MPFR_UNLIKELY (MPFR_IS_INF (te))) { @@ -119,13 +126,13 @@ mpfr_tanh (mpfr_ptr y, mpfr_srcptr xt , mpfr_rnd_t rnd_mode) break; } d = MPFR_GET_EXP (te); /* For Error calculation */ - mpfr_add_ui (t, te, 1, MPFR_RNDD); /* exp(2x) + 1*/ - mpfr_sub_ui (te, te, 1, MPFR_RNDU); /* exp(2x) - 1*/ + mpfr_add_ui (t, te, 1, MPFR_RNDD); /* exp(2x) + 1 */ + mpfr_sub_ui (te, te, 1, MPFR_RNDU); /* exp(2x) - 1 */ d = d - MPFR_GET_EXP (te); - mpfr_div (t, te, t, MPFR_RNDN); /* (exp(2x)-1)/(exp(2x)+1)*/ + mpfr_div (t, te, t, MPFR_RNDN); /* (exp(2x)-1)/(exp(2x)+1) */ - /* Calculation of the error */ - d = MAX(3, d + 1); + /* Calculation of the error, see algorithms.tex */ + d = MAX(4, d + 2); err = Nt - (d + 1); if (MPFR_LIKELY ((d <= Nt / 2) && MPFR_CAN_ROUND (t, err, Ny, rnd_mode))) diff --git a/tests/ttanh.c b/tests/ttanh.c index 90798d3ff..e2628a2e3 100644 --- a/tests/ttanh.c +++ b/tests/ttanh.c @@ -116,11 +116,31 @@ special_overflow (void) mpfr_clear (x); } +/* This test was generated from bad_cases, with input y=-7.778@-1 = -3823/8192. + For the x value below, we have atanh(y) < x, thus since tanh() is increasing, + y < tanh(x), and thus tanh(x) rounded towards zero should give -3822/8192. */ +static void +bug20171218 (void) +{ + mpfr_t x, y, z; + mpfr_init2 (x, 813); + mpfr_init2 (y, 12); + mpfr_init2 (z, 12); + mpfr_set_str (x, "-8.17cd20bfc17ae00935dc3abad8e17ab43d3ef7740c320798eefb93191f4a62dba9a2daa5efb6eace21130abd87e3ee2eadd2ad8ddae883d2f2db5dee1ac7ce3c59d16eca09e2ca3f21dc2a0386c037a0d3972e62d5b6e82446032020705553c566b1df24f40@-1", 16, MPFR_RNDN); + mpfr_tanh (y, x, MPFR_RNDZ); + mpfr_set_str (z, "-7.770@-1", 16, MPFR_RNDN); + MPFR_ASSERTN(mpfr_equal_p (y, z)); + mpfr_clear (x); + mpfr_clear (y); + mpfr_clear (z); +} + int main (int argc, char *argv[]) { tests_start_mpfr (); + bug20171218 (); special_overflow (); special (); |