summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzimmerma <zimmerma@280ebfd0-de03-0410-8827-d642c229c3f4>2017-12-18 08:51:06 +0000
committerzimmerma <zimmerma@280ebfd0-de03-0410-8827-d642c229c3f4>2017-12-18 08:51:06 +0000
commit974825f8f3023a6d9e5c75d6010000064dc2672e (patch)
tree9b9989f9c9df406eddcf41fd04fa3fffb6ab97ed
parentaf989821960f0e6d15d015480406301174b6dd03 (diff)
downloadmpfr-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.tex25
-rw-r--r--src/tanh.c19
-rw-r--r--tests/ttanh.c20
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 ();