Home | History | Annotate | Line # | Download | only in src
      1 /* mpfr_erf -- error function of a floating-point number
      2 
      3 Copyright 2001, 2003-2023 Free Software Foundation, Inc.
      4 Contributed by the AriC and Caramba projects, INRIA.
      5 
      6 This file is part of the GNU MPFR Library.
      7 
      8 The GNU MPFR Library is free software; you can redistribute it and/or modify
      9 it under the terms of the GNU Lesser General Public License as published by
     10 the Free Software Foundation; either version 3 of the License, or (at your
     11 option) any later version.
     12 
     13 The GNU MPFR Library is distributed in the hope that it will be useful, but
     14 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     15 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
     16 License for more details.
     17 
     18 You should have received a copy of the GNU Lesser General Public License
     19 along with the GNU MPFR Library; see the file COPYING.LESSER.  If not, see
     20 https://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc.,
     21 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */
     22 
     23 #define MPFR_NEED_LONGLONG_H
     24 #include "mpfr-impl.h"
     25 
     26 static int mpfr_erf_0 (mpfr_ptr, mpfr_srcptr, double, mpfr_rnd_t);
     27 
     28 int
     29 mpfr_erf (mpfr_ptr y, mpfr_srcptr x, mpfr_rnd_t rnd_mode)
     30 {
     31   mpfr_t xf;
     32   mp_limb_t xf_limb[(53 - 1) / GMP_NUMB_BITS + 1];
     33   int inex, large;
     34   MPFR_SAVE_EXPO_DECL (expo);
     35 
     36   MPFR_LOG_FUNC
     37     (("x[%Pd]=%.*Rg rnd=%d", mpfr_get_prec (x), mpfr_log_prec, x, rnd_mode),
     38      ("y[%Pd]=%.*Rg inexact=%d", mpfr_get_prec (y), mpfr_log_prec, y, inex));
     39 
     40   if (MPFR_UNLIKELY (MPFR_IS_SINGULAR (x)))
     41     {
     42       if (MPFR_IS_NAN (x))
     43         {
     44           MPFR_SET_NAN (y);
     45           MPFR_RET_NAN;
     46         }
     47       else if (MPFR_IS_INF (x)) /* erf(+inf) = +1, erf(-inf) = -1 */
     48         return mpfr_set_si (y, MPFR_INT_SIGN (x), MPFR_RNDN);
     49       else /* erf(+0) = +0, erf(-0) = -0 */
     50         {
     51           MPFR_ASSERTD (MPFR_IS_ZERO (x));
     52           return mpfr_set (y, x, MPFR_RNDN); /* should keep the sign of x */
     53         }
     54     }
     55 
     56   /* now x is neither NaN, Inf nor 0 */
     57 
     58   /* first try expansion at x=0 when x is small, or asymptotic expansion
     59      where x is large */
     60 
     61   MPFR_SAVE_EXPO_MARK (expo);
     62 
     63   /* around x=0, we have erf(x) = 2x/sqrt(Pi) (1 - x^2/3 + ...),
     64      with 1 - x^2/3 <= sqrt(Pi)*erf(x)/2/x <= 1 for x >= 0. This means that
     65      if x^2/3 < 2^(-PREC(y)-1) we can decide of the correct rounding,
     66      unless we have a worst-case for 2x/sqrt(Pi). */
     67   if (MPFR_EXP(x) < - (mpfr_exp_t) (MPFR_PREC(y) / 2))
     68     {
     69       /* we use 2x/sqrt(Pi) (1 - x^2/3) <= erf(x) <= 2x/sqrt(Pi) for x > 0
     70          and 2x/sqrt(Pi) <= erf(x) <= 2x/sqrt(Pi) (1 - x^2/3) for x < 0.
     71          In both cases |2x/sqrt(Pi) (1 - x^2/3)| <= |erf(x)| <= |2x/sqrt(Pi)|.
     72          We will compute l and h such that l <= |2x/sqrt(Pi) (1 - x^2/3)|
     73          and |2x/sqrt(Pi)| <= h. If l and h round to the same value to
     74          precision PREC(y) and rounding rnd_mode, then we are done. */
     75       mpfr_t l, h; /* lower and upper bounds for erf(x) */
     76       int ok, inex2;
     77 
     78       mpfr_init2 (l, MPFR_PREC(y) + 17);
     79       mpfr_init2 (h, MPFR_PREC(y) + 17);
     80       /* first compute l */
     81       mpfr_sqr (l, x, MPFR_RNDU);
     82       mpfr_div_ui (l, l, 3, MPFR_RNDU); /* upper bound on x^2/3 */
     83       mpfr_ui_sub (l, 1, l, MPFR_RNDZ); /* lower bound on 1 - x^2/3 */
     84       mpfr_const_pi (h, MPFR_RNDU); /* upper bound of Pi */
     85       mpfr_sqrt (h, h, MPFR_RNDU); /* upper bound on sqrt(Pi) */
     86       mpfr_div (l, l, h, MPFR_RNDZ); /* lower bound on 1/sqrt(Pi) (1 - x^2/3) */
     87       mpfr_mul_2ui (l, l, 1, MPFR_RNDZ); /* 2/sqrt(Pi) (1 - x^2/3) */
     88       mpfr_mul (l, l, x, MPFR_RNDZ); /* |l| is a lower bound on
     89                                        |2x/sqrt(Pi) (1 - x^2/3)| */
     90       /* now compute h */
     91       mpfr_const_pi (h, MPFR_RNDD); /* lower bound on Pi */
     92       mpfr_sqrt (h, h, MPFR_RNDD); /* lower bound on sqrt(Pi) */
     93       mpfr_div_2ui (h, h, 1, MPFR_RNDD); /* lower bound on sqrt(Pi)/2 */
     94       /* since sqrt(Pi)/2 < 1, the following should not underflow */
     95       mpfr_div (h, x, h, MPFR_IS_POS(x) ? MPFR_RNDU : MPFR_RNDD);
     96       /* round l and h to precision PREC(y) */
     97       inex = mpfr_prec_round (l, MPFR_PREC(y), rnd_mode);
     98       inex2 = mpfr_prec_round (h, MPFR_PREC(y), rnd_mode);
     99       /* Caution: we also need inex=inex2 (inex might be 0). */
    100       ok = SAME_SIGN (inex, inex2) && mpfr_equal_p (l, h);
    101       if (ok)
    102         mpfr_set (y, h, rnd_mode);
    103       mpfr_clear (l);
    104       mpfr_clear (h);
    105       if (ok)
    106         goto end;
    107       /* this test can still fail for small precision, for example
    108          for x=-0.100E-2 with a target precision of 3 bits, since
    109          the error term x^2/3 is not that small. */
    110     }
    111 
    112   MPFR_TMP_INIT1(xf_limb, xf, 53);
    113   mpfr_div (xf, x, __gmpfr_const_log2_RNDU, MPFR_RNDZ); /* round to zero
    114                         ensures we get a lower bound of |x/log(2)| */
    115   mpfr_mul (xf, xf, x, MPFR_RNDZ);
    116   large = mpfr_cmp_ui (xf, MPFR_PREC (y) + 1) > 0;
    117 
    118   /* when x goes to infinity, we have erf(x) = 1 - 1/sqrt(Pi)/exp(x^2)/x + ...
    119      and |erf(x) - 1| <= exp(-x^2) is true for any x >= 0, thus if
    120      exp(-x^2) < 2^(-PREC(y)-1) the result is 1 or 1-epsilon.
    121      This rewrites as x^2/log(2) > p+1. */
    122   if (MPFR_UNLIKELY (large))
    123     /* |erf x| = 1 or 1- */
    124     {
    125       mpfr_rnd_t rnd2 = MPFR_IS_POS (x) ? rnd_mode : MPFR_INVERT_RND(rnd_mode);
    126       if (rnd2 == MPFR_RNDN || rnd2 == MPFR_RNDU || rnd2 == MPFR_RNDA)
    127         {
    128           inex = MPFR_INT_SIGN (x);
    129           mpfr_set_si (y, inex, rnd2);
    130         }
    131       else /* round to zero */
    132         {
    133           inex = -MPFR_INT_SIGN (x);
    134           mpfr_setmax (y, 0); /* warning: setmax keeps the old sign of y */
    135           MPFR_SET_SAME_SIGN (y, x);
    136         }
    137     }
    138   else  /* use Taylor */
    139     {
    140       double xf2;
    141 
    142       /* FIXME: get rid of doubles/mpfr_get_d here */
    143       xf2 = mpfr_get_d (x, MPFR_RNDN);
    144       xf2 = xf2 * xf2; /* xf2 ~ x^2 */
    145       inex = mpfr_erf_0 (y, x, xf2, rnd_mode);
    146     }
    147 
    148  end:
    149   MPFR_SAVE_EXPO_FREE (expo);
    150   return mpfr_check_range (y, inex, rnd_mode);
    151 }
    152 
    153 /* return x*2^e */
    154 static double
    155 mul_2exp (double x, mpfr_exp_t e)
    156 {
    157   /* Most of the times, the argument is negative */
    158   if (MPFR_UNLIKELY (e > 0))
    159     {
    160       while (e--)
    161         x *= 2.0;
    162     }
    163   else
    164     {
    165       while (e <= -16)
    166         {
    167           x *= (1.0 / 65536.0);
    168           e += 16;
    169         }
    170       while (e++)
    171         x *= 0.5;
    172     }
    173 
    174   return x;
    175 }
    176 
    177 /* evaluates erf(x) using the expansion at x=0:
    178 
    179    erf(x) = 2/sqrt(Pi) * sum((-1)^k*x^(2k+1)/k!/(2k+1), k=0..infinity)
    180 
    181    Assumes x is neither NaN nor infinite nor zero.
    182    Assumes also that e*x^2 <= n (target precision).
    183  */
    184 static int
    185 mpfr_erf_0 (mpfr_ptr res, mpfr_srcptr x, double xf2, mpfr_rnd_t rnd_mode)
    186 {
    187   mpfr_prec_t n, m;
    188   mpfr_exp_t nuk, sigmak;
    189   mpfr_t y, s, t, u;
    190   unsigned int k;
    191   int inex;
    192   MPFR_GROUP_DECL (group);
    193   MPFR_ZIV_DECL (loop);
    194 
    195   n = MPFR_PREC (res); /* target precision */
    196 
    197   /* initial working precision */
    198   m = n + (mpfr_prec_t) (xf2 / LOG2) + 8 + MPFR_INT_CEIL_LOG2 (n);
    199 
    200   MPFR_GROUP_INIT_4(group, m, y, s, t, u);
    201 
    202   MPFR_ZIV_INIT (loop, m);
    203   for (;;)
    204     {
    205       mpfr_t tauk;
    206       mpfr_exp_t log2tauk;
    207 
    208       mpfr_sqr (y, x, MPFR_RNDU); /* err <= 1 ulp */
    209       mpfr_set_ui (s, 1, MPFR_RNDN);
    210       mpfr_set_ui (t, 1, MPFR_RNDN);
    211       mpfr_init2 (tauk, 53);
    212       mpfr_set_ui (tauk, 0, MPFR_RNDU);
    213 
    214       for (k = 1; ; k++)
    215         {
    216           mpfr_mul (t, y, t, MPFR_RNDU);
    217           mpfr_div_ui (t, t, k, MPFR_RNDU);
    218           mpfr_div_ui (u, t, 2 * k + 1, MPFR_RNDU);
    219           sigmak = MPFR_GET_EXP (s);
    220           if (k % 2)
    221             mpfr_sub (s, s, u, MPFR_RNDN);
    222           else
    223             mpfr_add (s, s, u, MPFR_RNDN);
    224           sigmak -= MPFR_GET_EXP(s);
    225           nuk = MPFR_GET_EXP(u) - MPFR_GET_EXP(s);
    226 
    227           if ((nuk < - (mpfr_exp_t) m) && (k >= xf2))
    228             break;
    229 
    230           /* tauk <- 1/2 + tauk * 2^sigmak + (1+8k)*2^nuk */
    231           mpfr_mul_2si (tauk, tauk, sigmak, MPFR_RNDU);
    232           mpfr_add_d (tauk, tauk, 0.5 + mul_2exp (1.0 + 8.0 * (double) k, nuk),
    233                       MPFR_RNDU);
    234         }
    235 
    236       mpfr_mul (s, x, s, MPFR_RNDU);
    237       MPFR_SET_EXP (s, MPFR_GET_EXP (s) + 1);
    238 
    239       mpfr_const_pi (t, MPFR_RNDZ);
    240       mpfr_sqrt (t, t, MPFR_RNDZ);
    241       mpfr_div (s, s, t, MPFR_RNDN);
    242       /* tauk = 4 * tauk + 11: final ulp-error on s */
    243       mpfr_mul_2ui (tauk, tauk, 2, MPFR_RNDU);
    244       mpfr_add_ui (tauk, tauk, 11, MPFR_RNDU);
    245       /* In practice, one should not get an infinity, as it would require
    246          a huge precision and a lot of time, but just in case... */
    247       MPFR_ASSERTN (!MPFR_IS_INF (tauk));
    248       log2tauk = MPFR_GET_EXP (tauk);
    249       mpfr_clear (tauk);
    250 
    251       if (MPFR_LIKELY (MPFR_CAN_ROUND (s, m - log2tauk, n, rnd_mode)))
    252         break;
    253 
    254       /* Actualisation of the precision */
    255       MPFR_ZIV_NEXT (loop, m);
    256       MPFR_GROUP_REPREC_4 (group, m, y, s, t, u);
    257     }
    258   MPFR_ZIV_FREE (loop);
    259 
    260   inex = mpfr_set (res, s, rnd_mode);
    261 
    262   MPFR_GROUP_CLEAR (group);
    263 
    264   return inex;
    265 }
    266