xref: /dflybsd-src/contrib/mpfr/src/erf.c (revision 2786097444a0124b5d33763854de247e230c6629)
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