14a238c70SJohn Marino /* mpfr_erf -- error function of a floating-point number
24a238c70SJohn Marino
3*ab6d115fSJohn Marino Copyright 2001, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
4*ab6d115fSJohn Marino Contributed by the AriC and Caramel projects, INRIA.
54a238c70SJohn Marino
64a238c70SJohn Marino This file is part of the GNU MPFR Library.
74a238c70SJohn Marino
84a238c70SJohn Marino The GNU MPFR Library is free software; you can redistribute it and/or modify
94a238c70SJohn Marino it under the terms of the GNU Lesser General Public License as published by
104a238c70SJohn Marino the Free Software Foundation; either version 3 of the License, or (at your
114a238c70SJohn Marino option) any later version.
124a238c70SJohn Marino
134a238c70SJohn Marino The GNU MPFR Library is distributed in the hope that it will be useful, but
144a238c70SJohn Marino WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
154a238c70SJohn Marino or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
164a238c70SJohn Marino License for more details.
174a238c70SJohn Marino
184a238c70SJohn Marino You should have received a copy of the GNU Lesser General Public License
194a238c70SJohn Marino along with the GNU MPFR Library; see the file COPYING.LESSER. If not, see
204a238c70SJohn Marino http://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc.,
214a238c70SJohn Marino 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */
224a238c70SJohn Marino
234a238c70SJohn Marino #define MPFR_NEED_LONGLONG_H
244a238c70SJohn Marino #include "mpfr-impl.h"
254a238c70SJohn Marino
264a238c70SJohn Marino #define EXP1 2.71828182845904523536 /* exp(1) */
274a238c70SJohn Marino
284a238c70SJohn Marino static int mpfr_erf_0 (mpfr_ptr, mpfr_srcptr, double, mpfr_rnd_t);
294a238c70SJohn Marino
304a238c70SJohn Marino int
mpfr_erf(mpfr_ptr y,mpfr_srcptr x,mpfr_rnd_t rnd_mode)314a238c70SJohn Marino mpfr_erf (mpfr_ptr y, mpfr_srcptr x, mpfr_rnd_t rnd_mode)
324a238c70SJohn Marino {
334a238c70SJohn Marino mpfr_t xf;
344a238c70SJohn Marino int inex, large;
354a238c70SJohn Marino MPFR_SAVE_EXPO_DECL (expo);
364a238c70SJohn Marino
374a238c70SJohn Marino MPFR_LOG_FUNC
384a238c70SJohn Marino (("x[%Pu]=%.*Rg rnd=%d", mpfr_get_prec (x), mpfr_log_prec, x, rnd_mode),
394a238c70SJohn Marino ("y[%Pu]=%.*Rg inexact=%d", mpfr_get_prec (y), mpfr_log_prec, y, inex));
404a238c70SJohn Marino
414a238c70SJohn Marino if (MPFR_UNLIKELY (MPFR_IS_SINGULAR (x)))
424a238c70SJohn Marino {
434a238c70SJohn Marino if (MPFR_IS_NAN (x))
444a238c70SJohn Marino {
454a238c70SJohn Marino MPFR_SET_NAN (y);
464a238c70SJohn Marino MPFR_RET_NAN;
474a238c70SJohn Marino }
484a238c70SJohn Marino else if (MPFR_IS_INF (x)) /* erf(+inf) = +1, erf(-inf) = -1 */
494a238c70SJohn Marino return mpfr_set_si (y, MPFR_INT_SIGN (x), MPFR_RNDN);
504a238c70SJohn Marino else /* erf(+0) = +0, erf(-0) = -0 */
514a238c70SJohn Marino {
524a238c70SJohn Marino MPFR_ASSERTD (MPFR_IS_ZERO (x));
534a238c70SJohn Marino return mpfr_set (y, x, MPFR_RNDN); /* should keep the sign of x */
544a238c70SJohn Marino }
554a238c70SJohn Marino }
564a238c70SJohn Marino
574a238c70SJohn Marino /* now x is neither NaN, Inf nor 0 */
584a238c70SJohn Marino
594a238c70SJohn Marino /* first try expansion at x=0 when x is small, or asymptotic expansion
604a238c70SJohn Marino where x is large */
614a238c70SJohn Marino
624a238c70SJohn Marino MPFR_SAVE_EXPO_MARK (expo);
634a238c70SJohn Marino
644a238c70SJohn Marino /* around x=0, we have erf(x) = 2x/sqrt(Pi) (1 - x^2/3 + ...),
654a238c70SJohn Marino with 1 - x^2/3 <= sqrt(Pi)*erf(x)/2/x <= 1 for x >= 0. This means that
664a238c70SJohn Marino if x^2/3 < 2^(-PREC(y)-1) we can decide of the correct rounding,
674a238c70SJohn Marino unless we have a worst-case for 2x/sqrt(Pi). */
684a238c70SJohn Marino if (MPFR_EXP(x) < - (mpfr_exp_t) (MPFR_PREC(y) / 2))
694a238c70SJohn Marino {
704a238c70SJohn Marino /* we use 2x/sqrt(Pi) (1 - x^2/3) <= erf(x) <= 2x/sqrt(Pi) for x > 0
714a238c70SJohn Marino and 2x/sqrt(Pi) <= erf(x) <= 2x/sqrt(Pi) (1 - x^2/3) for x < 0.
724a238c70SJohn Marino In both cases |2x/sqrt(Pi) (1 - x^2/3)| <= |erf(x)| <= |2x/sqrt(Pi)|.
734a238c70SJohn Marino We will compute l and h such that l <= |2x/sqrt(Pi) (1 - x^2/3)|
744a238c70SJohn Marino and |2x/sqrt(Pi)| <= h. If l and h round to the same value to
754a238c70SJohn Marino precision PREC(y) and rounding rnd_mode, then we are done. */
764a238c70SJohn Marino mpfr_t l, h; /* lower and upper bounds for erf(x) */
774a238c70SJohn Marino int ok, inex2;
784a238c70SJohn Marino
794a238c70SJohn Marino mpfr_init2 (l, MPFR_PREC(y) + 17);
804a238c70SJohn Marino mpfr_init2 (h, MPFR_PREC(y) + 17);
814a238c70SJohn Marino /* first compute l */
824a238c70SJohn Marino mpfr_mul (l, x, x, MPFR_RNDU);
834a238c70SJohn Marino mpfr_div_ui (l, l, 3, MPFR_RNDU); /* upper bound on x^2/3 */
844a238c70SJohn Marino mpfr_ui_sub (l, 1, l, MPFR_RNDZ); /* lower bound on 1 - x^2/3 */
854a238c70SJohn Marino mpfr_const_pi (h, MPFR_RNDU); /* upper bound of Pi */
864a238c70SJohn Marino mpfr_sqrt (h, h, MPFR_RNDU); /* upper bound on sqrt(Pi) */
874a238c70SJohn Marino mpfr_div (l, l, h, MPFR_RNDZ); /* lower bound on 1/sqrt(Pi) (1 - x^2/3) */
884a238c70SJohn Marino mpfr_mul_2ui (l, l, 1, MPFR_RNDZ); /* 2/sqrt(Pi) (1 - x^2/3) */
894a238c70SJohn Marino mpfr_mul (l, l, x, MPFR_RNDZ); /* |l| is a lower bound on
904a238c70SJohn Marino |2x/sqrt(Pi) (1 - x^2/3)| */
914a238c70SJohn Marino /* now compute h */
924a238c70SJohn Marino mpfr_const_pi (h, MPFR_RNDD); /* lower bound on Pi */
934a238c70SJohn Marino mpfr_sqrt (h, h, MPFR_RNDD); /* lower bound on sqrt(Pi) */
944a238c70SJohn Marino mpfr_div_2ui (h, h, 1, MPFR_RNDD); /* lower bound on sqrt(Pi)/2 */
954a238c70SJohn Marino /* since sqrt(Pi)/2 < 1, the following should not underflow */
964a238c70SJohn Marino mpfr_div (h, x, h, MPFR_IS_POS(x) ? MPFR_RNDU : MPFR_RNDD);
974a238c70SJohn Marino /* round l and h to precision PREC(y) */
984a238c70SJohn Marino inex = mpfr_prec_round (l, MPFR_PREC(y), rnd_mode);
994a238c70SJohn Marino inex2 = mpfr_prec_round (h, MPFR_PREC(y), rnd_mode);
1004a238c70SJohn Marino /* Caution: we also need inex=inex2 (inex might be 0). */
1014a238c70SJohn Marino ok = SAME_SIGN (inex, inex2) && mpfr_cmp (l, h) == 0;
1024a238c70SJohn Marino if (ok)
1034a238c70SJohn Marino mpfr_set (y, h, rnd_mode);
1044a238c70SJohn Marino mpfr_clear (l);
1054a238c70SJohn Marino mpfr_clear (h);
1064a238c70SJohn Marino if (ok)
1074a238c70SJohn Marino goto end;
1084a238c70SJohn Marino /* this test can still fail for small precision, for example
1094a238c70SJohn Marino for x=-0.100E-2 with a target precision of 3 bits, since
1104a238c70SJohn Marino the error term x^2/3 is not that small. */
1114a238c70SJohn Marino }
1124a238c70SJohn Marino
1134a238c70SJohn Marino mpfr_init2 (xf, 53);
1144a238c70SJohn Marino mpfr_const_log2 (xf, MPFR_RNDU);
1154a238c70SJohn Marino mpfr_div (xf, x, xf, MPFR_RNDZ); /* round to zero ensures we get a lower
1164a238c70SJohn Marino bound of |x/log(2)| */
1174a238c70SJohn Marino mpfr_mul (xf, xf, x, MPFR_RNDZ);
1184a238c70SJohn Marino large = mpfr_cmp_ui (xf, MPFR_PREC (y) + 1) > 0;
1194a238c70SJohn Marino mpfr_clear (xf);
1204a238c70SJohn Marino
1214a238c70SJohn Marino /* when x goes to infinity, we have erf(x) = 1 - 1/sqrt(Pi)/exp(x^2)/x + ...
1224a238c70SJohn Marino and |erf(x) - 1| <= exp(-x^2) is true for any x >= 0, thus if
1234a238c70SJohn Marino exp(-x^2) < 2^(-PREC(y)-1) the result is 1 or 1-epsilon.
1244a238c70SJohn Marino This rewrites as x^2/log(2) > p+1. */
1254a238c70SJohn Marino if (MPFR_UNLIKELY (large))
1264a238c70SJohn Marino /* |erf x| = 1 or 1- */
1274a238c70SJohn Marino {
1284a238c70SJohn Marino mpfr_rnd_t rnd2 = MPFR_IS_POS (x) ? rnd_mode : MPFR_INVERT_RND(rnd_mode);
1294a238c70SJohn Marino if (rnd2 == MPFR_RNDN || rnd2 == MPFR_RNDU || rnd2 == MPFR_RNDA)
1304a238c70SJohn Marino {
1314a238c70SJohn Marino inex = MPFR_INT_SIGN (x);
1324a238c70SJohn Marino mpfr_set_si (y, inex, rnd2);
1334a238c70SJohn Marino }
1344a238c70SJohn Marino else /* round to zero */
1354a238c70SJohn Marino {
1364a238c70SJohn Marino inex = -MPFR_INT_SIGN (x);
1374a238c70SJohn Marino mpfr_setmax (y, 0); /* warning: setmax keeps the old sign of y */
1384a238c70SJohn Marino MPFR_SET_SAME_SIGN (y, x);
1394a238c70SJohn Marino }
1404a238c70SJohn Marino }
1414a238c70SJohn Marino else /* use Taylor */
1424a238c70SJohn Marino {
1434a238c70SJohn Marino double xf2;
1444a238c70SJohn Marino
1454a238c70SJohn Marino /* FIXME: get rid of doubles/mpfr_get_d here */
1464a238c70SJohn Marino xf2 = mpfr_get_d (x, MPFR_RNDN);
1474a238c70SJohn Marino xf2 = xf2 * xf2; /* xf2 ~ x^2 */
1484a238c70SJohn Marino inex = mpfr_erf_0 (y, x, xf2, rnd_mode);
1494a238c70SJohn Marino }
1504a238c70SJohn Marino
1514a238c70SJohn Marino end:
1524a238c70SJohn Marino MPFR_SAVE_EXPO_FREE (expo);
1534a238c70SJohn Marino return mpfr_check_range (y, inex, rnd_mode);
1544a238c70SJohn Marino }
1554a238c70SJohn Marino
1564a238c70SJohn Marino /* return x*2^e */
1574a238c70SJohn Marino static double
mul_2exp(double x,mpfr_exp_t e)1584a238c70SJohn Marino mul_2exp (double x, mpfr_exp_t e)
1594a238c70SJohn Marino {
1604a238c70SJohn Marino if (e > 0)
1614a238c70SJohn Marino {
1624a238c70SJohn Marino while (e--)
1634a238c70SJohn Marino x *= 2.0;
1644a238c70SJohn Marino }
1654a238c70SJohn Marino else
1664a238c70SJohn Marino {
1674a238c70SJohn Marino while (e++)
1684a238c70SJohn Marino x /= 2.0;
1694a238c70SJohn Marino }
1704a238c70SJohn Marino
1714a238c70SJohn Marino return x;
1724a238c70SJohn Marino }
1734a238c70SJohn Marino
1744a238c70SJohn Marino /* evaluates erf(x) using the expansion at x=0:
1754a238c70SJohn Marino
1764a238c70SJohn Marino erf(x) = 2/sqrt(Pi) * sum((-1)^k*x^(2k+1)/k!/(2k+1), k=0..infinity)
1774a238c70SJohn Marino
1784a238c70SJohn Marino Assumes x is neither NaN nor infinite nor zero.
1794a238c70SJohn Marino Assumes also that e*x^2 <= n (target precision).
1804a238c70SJohn Marino */
1814a238c70SJohn Marino static int
mpfr_erf_0(mpfr_ptr res,mpfr_srcptr x,double xf2,mpfr_rnd_t rnd_mode)1824a238c70SJohn Marino mpfr_erf_0 (mpfr_ptr res, mpfr_srcptr x, double xf2, mpfr_rnd_t rnd_mode)
1834a238c70SJohn Marino {
1844a238c70SJohn Marino mpfr_prec_t n, m;
1854a238c70SJohn Marino mpfr_exp_t nuk, sigmak;
1864a238c70SJohn Marino double tauk;
1874a238c70SJohn Marino mpfr_t y, s, t, u;
1884a238c70SJohn Marino unsigned int k;
1894a238c70SJohn Marino int log2tauk;
1904a238c70SJohn Marino int inex;
1914a238c70SJohn Marino MPFR_ZIV_DECL (loop);
1924a238c70SJohn Marino
1934a238c70SJohn Marino n = MPFR_PREC (res); /* target precision */
1944a238c70SJohn Marino
1954a238c70SJohn Marino /* initial working precision */
1964a238c70SJohn Marino m = n + (mpfr_prec_t) (xf2 / LOG2) + 8 + MPFR_INT_CEIL_LOG2 (n);
1974a238c70SJohn Marino
1984a238c70SJohn Marino mpfr_init2 (y, m);
1994a238c70SJohn Marino mpfr_init2 (s, m);
2004a238c70SJohn Marino mpfr_init2 (t, m);
2014a238c70SJohn Marino mpfr_init2 (u, m);
2024a238c70SJohn Marino
2034a238c70SJohn Marino MPFR_ZIV_INIT (loop, m);
2044a238c70SJohn Marino for (;;)
2054a238c70SJohn Marino {
2064a238c70SJohn Marino mpfr_mul (y, x, x, MPFR_RNDU); /* err <= 1 ulp */
2074a238c70SJohn Marino mpfr_set_ui (s, 1, MPFR_RNDN);
2084a238c70SJohn Marino mpfr_set_ui (t, 1, MPFR_RNDN);
2094a238c70SJohn Marino tauk = 0.0;
2104a238c70SJohn Marino
2114a238c70SJohn Marino for (k = 1; ; k++)
2124a238c70SJohn Marino {
2134a238c70SJohn Marino mpfr_mul (t, y, t, MPFR_RNDU);
2144a238c70SJohn Marino mpfr_div_ui (t, t, k, MPFR_RNDU);
2154a238c70SJohn Marino mpfr_div_ui (u, t, 2 * k + 1, MPFR_RNDU);
2164a238c70SJohn Marino sigmak = MPFR_GET_EXP (s);
2174a238c70SJohn Marino if (k % 2)
2184a238c70SJohn Marino mpfr_sub (s, s, u, MPFR_RNDN);
2194a238c70SJohn Marino else
2204a238c70SJohn Marino mpfr_add (s, s, u, MPFR_RNDN);
2214a238c70SJohn Marino sigmak -= MPFR_GET_EXP(s);
2224a238c70SJohn Marino nuk = MPFR_GET_EXP(u) - MPFR_GET_EXP(s);
2234a238c70SJohn Marino
2244a238c70SJohn Marino if ((nuk < - (mpfr_exp_t) m) && ((double) k >= xf2))
2254a238c70SJohn Marino break;
2264a238c70SJohn Marino
2274a238c70SJohn Marino /* tauk <- 1/2 + tauk * 2^sigmak + (1+8k)*2^nuk */
2284a238c70SJohn Marino tauk = 0.5 + mul_2exp (tauk, sigmak)
2294a238c70SJohn Marino + mul_2exp (1.0 + 8.0 * (double) k, nuk);
2304a238c70SJohn Marino }
2314a238c70SJohn Marino
2324a238c70SJohn Marino mpfr_mul (s, x, s, MPFR_RNDU);
2334a238c70SJohn Marino MPFR_SET_EXP (s, MPFR_GET_EXP (s) + 1);
2344a238c70SJohn Marino
2354a238c70SJohn Marino mpfr_const_pi (t, MPFR_RNDZ);
2364a238c70SJohn Marino mpfr_sqrt (t, t, MPFR_RNDZ);
2374a238c70SJohn Marino mpfr_div (s, s, t, MPFR_RNDN);
2384a238c70SJohn Marino tauk = 4.0 * tauk + 11.0; /* final ulp-error on s */
2394a238c70SJohn Marino log2tauk = __gmpfr_ceil_log2 (tauk);
2404a238c70SJohn Marino
2414a238c70SJohn Marino if (MPFR_LIKELY (MPFR_CAN_ROUND (s, m - log2tauk, n, rnd_mode)))
2424a238c70SJohn Marino break;
2434a238c70SJohn Marino
2444a238c70SJohn Marino /* Actualisation of the precision */
2454a238c70SJohn Marino MPFR_ZIV_NEXT (loop, m);
2464a238c70SJohn Marino mpfr_set_prec (y, m);
2474a238c70SJohn Marino mpfr_set_prec (s, m);
2484a238c70SJohn Marino mpfr_set_prec (t, m);
2494a238c70SJohn Marino mpfr_set_prec (u, m);
2504a238c70SJohn Marino
2514a238c70SJohn Marino }
2524a238c70SJohn Marino MPFR_ZIV_FREE (loop);
2534a238c70SJohn Marino
2544a238c70SJohn Marino inex = mpfr_set (res, s, rnd_mode);
2554a238c70SJohn Marino
2564a238c70SJohn Marino mpfr_clear (y);
2574a238c70SJohn Marino mpfr_clear (t);
2584a238c70SJohn Marino mpfr_clear (u);
2594a238c70SJohn Marino mpfr_clear (s);
2604a238c70SJohn Marino
2614a238c70SJohn Marino return inex;
2624a238c70SJohn Marino }
263