xref: /freebsd/contrib/arm-optimized-routines/math/erf.c (revision 072a4ba82a01476eaee33781ccd241033eefcf0b)
131914882SAlex Richardson /*
231914882SAlex Richardson  * Double-precision erf(x) function.
331914882SAlex Richardson  *
431914882SAlex Richardson  * Copyright (c) 2020, Arm Limited.
5*072a4ba8SAndrew Turner  * SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception
631914882SAlex Richardson  */
731914882SAlex Richardson 
831914882SAlex Richardson #include "math_config.h"
931914882SAlex Richardson #include <math.h>
1031914882SAlex Richardson #include <stdint.h>
1131914882SAlex Richardson 
1231914882SAlex Richardson #define TwoOverSqrtPiMinusOne 0x1.06eba8214db69p-3
1331914882SAlex Richardson #define C 0x1.b0ac16p-1
1431914882SAlex Richardson #define PA __erf_data.erf_poly_A
1531914882SAlex Richardson #define NA __erf_data.erf_ratio_N_A
1631914882SAlex Richardson #define DA __erf_data.erf_ratio_D_A
1731914882SAlex Richardson #define NB __erf_data.erf_ratio_N_B
1831914882SAlex Richardson #define DB __erf_data.erf_ratio_D_B
1931914882SAlex Richardson #define PC __erf_data.erfc_poly_C
2031914882SAlex Richardson #define PD __erf_data.erfc_poly_D
2131914882SAlex Richardson #define PE __erf_data.erfc_poly_E
2231914882SAlex Richardson #define PF __erf_data.erfc_poly_F
2331914882SAlex Richardson 
2431914882SAlex Richardson /* Top 32 bits of a double.  */
2531914882SAlex Richardson static inline uint32_t
top32(double x)2631914882SAlex Richardson top32 (double x)
2731914882SAlex Richardson {
2831914882SAlex Richardson   return asuint64 (x) >> 32;
2931914882SAlex Richardson }
3031914882SAlex Richardson 
3131914882SAlex Richardson /* Fast erf implementation using a mix of
3231914882SAlex Richardson    rational and polynomial approximations.
3331914882SAlex Richardson    Highest measured error is 1.01 ULPs at 0x1.39956ac43382fp+0.  */
3431914882SAlex Richardson double
erf(double x)3531914882SAlex Richardson erf (double x)
3631914882SAlex Richardson {
3731914882SAlex Richardson   /* Get top word and sign.  */
3831914882SAlex Richardson   uint32_t ix = top32 (x);
3931914882SAlex Richardson   uint32_t ia = ix & 0x7fffffff;
4031914882SAlex Richardson   uint32_t sign = ix >> 31;
4131914882SAlex Richardson 
4231914882SAlex Richardson   /* Normalized and subnormal cases */
4331914882SAlex Richardson   if (ia < 0x3feb0000)
4431914882SAlex Richardson     { /* a = |x| < 0.84375.  */
4531914882SAlex Richardson 
4631914882SAlex Richardson       if (ia < 0x3e300000)
4731914882SAlex Richardson 	{ /* a < 2^(-28).  */
4831914882SAlex Richardson 	  if (ia < 0x00800000)
4931914882SAlex Richardson 	    { /* a < 2^(-1015).  */
5031914882SAlex Richardson 	      double y =  fma (TwoOverSqrtPiMinusOne, x, x);
5131914882SAlex Richardson 	      return check_uflow (y);
5231914882SAlex Richardson 	    }
5331914882SAlex Richardson 	  return x + TwoOverSqrtPiMinusOne * x;
5431914882SAlex Richardson 	}
5531914882SAlex Richardson 
5631914882SAlex Richardson       double x2 = x * x;
5731914882SAlex Richardson 
5831914882SAlex Richardson       if (ia < 0x3fe00000)
5931914882SAlex Richardson 	{ /* a < 0.5  - Use polynomial approximation.  */
6031914882SAlex Richardson 	  double r1 = fma (x2, PA[1], PA[0]);
6131914882SAlex Richardson 	  double r2 = fma (x2, PA[3], PA[2]);
6231914882SAlex Richardson 	  double r3 = fma (x2, PA[5], PA[4]);
6331914882SAlex Richardson 	  double r4 = fma (x2, PA[7], PA[6]);
6431914882SAlex Richardson 	  double r5 = fma (x2, PA[9], PA[8]);
6531914882SAlex Richardson 	  double x4 = x2 * x2;
6631914882SAlex Richardson 	  double r = r5;
6731914882SAlex Richardson 	  r = fma (x4, r, r4);
6831914882SAlex Richardson 	  r = fma (x4, r, r3);
6931914882SAlex Richardson 	  r = fma (x4, r, r2);
7031914882SAlex Richardson 	  r = fma (x4, r, r1);
7131914882SAlex Richardson 	  return fma (r, x, x); /* This fma is crucial for accuracy.  */
7231914882SAlex Richardson 	}
7331914882SAlex Richardson       else
7431914882SAlex Richardson 	{ /* 0.5 <= a < 0.84375 - Use rational approximation.  */
7531914882SAlex Richardson 	  double x4, x8, r1n, r2n, r1d, r2d, r3d;
7631914882SAlex Richardson 
7731914882SAlex Richardson 	  r1n = fma (x2, NA[1], NA[0]);
7831914882SAlex Richardson 	  x4 = x2 * x2;
7931914882SAlex Richardson 	  r2n = fma (x2, NA[3], NA[2]);
8031914882SAlex Richardson 	  x8 = x4 * x4;
8131914882SAlex Richardson 	  r1d = fma (x2, DA[0], 1.0);
8231914882SAlex Richardson 	  r2d = fma (x2, DA[2], DA[1]);
8331914882SAlex Richardson 	  r3d = fma (x2, DA[4], DA[3]);
8431914882SAlex Richardson 	  double P = r1n + x4 * r2n + x8 * NA[4];
8531914882SAlex Richardson 	  double Q = r1d + x4 * r2d + x8 * r3d;
8631914882SAlex Richardson 	  return fma (P / Q, x, x);
8731914882SAlex Richardson 	}
8831914882SAlex Richardson     }
8931914882SAlex Richardson   else if (ia < 0x3ff40000)
9031914882SAlex Richardson     { /* 0.84375 <= |x| < 1.25.  */
9131914882SAlex Richardson       double a2, a4, a6, r1n, r2n, r3n, r4n, r1d, r2d, r3d, r4d;
9231914882SAlex Richardson       double a = fabs (x) - 1.0;
9331914882SAlex Richardson       r1n = fma (a, NB[1], NB[0]);
9431914882SAlex Richardson       a2 = a * a;
9531914882SAlex Richardson       r1d = fma (a, DB[0], 1.0);
9631914882SAlex Richardson       a4 = a2 * a2;
9731914882SAlex Richardson       r2n = fma (a, NB[3], NB[2]);
9831914882SAlex Richardson       a6 = a4 * a2;
9931914882SAlex Richardson       r2d = fma (a, DB[2], DB[1]);
10031914882SAlex Richardson       r3n = fma (a, NB[5], NB[4]);
10131914882SAlex Richardson       r3d = fma (a, DB[4], DB[3]);
10231914882SAlex Richardson       r4n = NB[6];
10331914882SAlex Richardson       r4d = DB[5];
10431914882SAlex Richardson       double P = r1n + a2 * r2n + a4 * r3n + a6 * r4n;
10531914882SAlex Richardson       double Q = r1d + a2 * r2d + a4 * r3d + a6 * r4d;
10631914882SAlex Richardson       if (sign)
10731914882SAlex Richardson 	return -C - P / Q;
10831914882SAlex Richardson       else
10931914882SAlex Richardson 	return C + P / Q;
11031914882SAlex Richardson     }
11131914882SAlex Richardson   else if (ia < 0x40000000)
11231914882SAlex Richardson     { /* 1.25 <= |x| < 2.0.  */
11331914882SAlex Richardson       double a = fabs (x);
11431914882SAlex Richardson       a = a - 1.25;
11531914882SAlex Richardson 
11631914882SAlex Richardson       double r1 = fma (a, PC[1], PC[0]);
11731914882SAlex Richardson       double r2 = fma (a, PC[3], PC[2]);
11831914882SAlex Richardson       double r3 = fma (a, PC[5], PC[4]);
11931914882SAlex Richardson       double r4 = fma (a, PC[7], PC[6]);
12031914882SAlex Richardson       double r5 = fma (a, PC[9], PC[8]);
12131914882SAlex Richardson       double r6 = fma (a, PC[11], PC[10]);
12231914882SAlex Richardson       double r7 = fma (a, PC[13], PC[12]);
12331914882SAlex Richardson       double r8 = fma (a, PC[15], PC[14]);
12431914882SAlex Richardson 
12531914882SAlex Richardson       double a2 = a * a;
12631914882SAlex Richardson 
12731914882SAlex Richardson       double r = r8;
12831914882SAlex Richardson       r = fma (a2, r, r7);
12931914882SAlex Richardson       r = fma (a2, r, r6);
13031914882SAlex Richardson       r = fma (a2, r, r5);
13131914882SAlex Richardson       r = fma (a2, r, r4);
13231914882SAlex Richardson       r = fma (a2, r, r3);
13331914882SAlex Richardson       r = fma (a2, r, r2);
13431914882SAlex Richardson       r = fma (a2, r, r1);
13531914882SAlex Richardson 
13631914882SAlex Richardson       if (sign)
13731914882SAlex Richardson 	return -1.0 + r;
13831914882SAlex Richardson       else
13931914882SAlex Richardson 	return 1.0 - r;
14031914882SAlex Richardson     }
14131914882SAlex Richardson   else if (ia < 0x400a0000)
14231914882SAlex Richardson     { /* 2 <= |x| < 3.25.  */
14331914882SAlex Richardson       double a = fabs (x);
14431914882SAlex Richardson       a = fma (0.5, a, -1.0);
14531914882SAlex Richardson 
14631914882SAlex Richardson       double r1 = fma (a, PD[1], PD[0]);
14731914882SAlex Richardson       double r2 = fma (a, PD[3], PD[2]);
14831914882SAlex Richardson       double r3 = fma (a, PD[5], PD[4]);
14931914882SAlex Richardson       double r4 = fma (a, PD[7], PD[6]);
15031914882SAlex Richardson       double r5 = fma (a, PD[9], PD[8]);
15131914882SAlex Richardson       double r6 = fma (a, PD[11], PD[10]);
15231914882SAlex Richardson       double r7 = fma (a, PD[13], PD[12]);
15331914882SAlex Richardson       double r8 = fma (a, PD[15], PD[14]);
15431914882SAlex Richardson       double r9 = fma (a, PD[17], PD[16]);
15531914882SAlex Richardson 
15631914882SAlex Richardson       double a2 = a * a;
15731914882SAlex Richardson 
15831914882SAlex Richardson       double r = r9;
15931914882SAlex Richardson       r = fma (a2, r, r8);
16031914882SAlex Richardson       r = fma (a2, r, r7);
16131914882SAlex Richardson       r = fma (a2, r, r6);
16231914882SAlex Richardson       r = fma (a2, r, r5);
16331914882SAlex Richardson       r = fma (a2, r, r4);
16431914882SAlex Richardson       r = fma (a2, r, r3);
16531914882SAlex Richardson       r = fma (a2, r, r2);
16631914882SAlex Richardson       r = fma (a2, r, r1);
16731914882SAlex Richardson 
16831914882SAlex Richardson       if (sign)
16931914882SAlex Richardson 	return -1.0 + r;
17031914882SAlex Richardson       else
17131914882SAlex Richardson 	return 1.0 - r;
17231914882SAlex Richardson     }
17331914882SAlex Richardson   else if (ia < 0x40100000)
17431914882SAlex Richardson     { /* 3.25 <= |x| < 4.0.  */
17531914882SAlex Richardson       double a = fabs (x);
17631914882SAlex Richardson       a = a - 3.25;
17731914882SAlex Richardson 
17831914882SAlex Richardson       double r1 = fma (a, PE[1], PE[0]);
17931914882SAlex Richardson       double r2 = fma (a, PE[3], PE[2]);
18031914882SAlex Richardson       double r3 = fma (a, PE[5], PE[4]);
18131914882SAlex Richardson       double r4 = fma (a, PE[7], PE[6]);
18231914882SAlex Richardson       double r5 = fma (a, PE[9], PE[8]);
18331914882SAlex Richardson       double r6 = fma (a, PE[11], PE[10]);
18431914882SAlex Richardson       double r7 = fma (a, PE[13], PE[12]);
18531914882SAlex Richardson 
18631914882SAlex Richardson       double a2 = a * a;
18731914882SAlex Richardson 
18831914882SAlex Richardson       double r = r7;
18931914882SAlex Richardson       r = fma (a2, r, r6);
19031914882SAlex Richardson       r = fma (a2, r, r5);
19131914882SAlex Richardson       r = fma (a2, r, r4);
19231914882SAlex Richardson       r = fma (a2, r, r3);
19331914882SAlex Richardson       r = fma (a2, r, r2);
19431914882SAlex Richardson       r = fma (a2, r, r1);
19531914882SAlex Richardson 
19631914882SAlex Richardson       if (sign)
19731914882SAlex Richardson 	return -1.0 + r;
19831914882SAlex Richardson       else
19931914882SAlex Richardson 	return 1.0 - r;
20031914882SAlex Richardson     }
20131914882SAlex Richardson   else if (ia < 0x4017a000)
20231914882SAlex Richardson     { /* 4 <= |x| < 5.90625.  */
20331914882SAlex Richardson       double a = fabs (x);
20431914882SAlex Richardson       a = fma (0.5, a, -2.0);
20531914882SAlex Richardson 
20631914882SAlex Richardson       double r1 = fma (a, PF[1], PF[0]);
20731914882SAlex Richardson       double r2 = fma (a, PF[3], PF[2]);
20831914882SAlex Richardson       double r3 = fma (a, PF[5], PF[4]);
20931914882SAlex Richardson       double r4 = fma (a, PF[7], PF[6]);
21031914882SAlex Richardson       double r5 = fma (a, PF[9], PF[8]);
21131914882SAlex Richardson       double r6 = fma (a, PF[11], PF[10]);
21231914882SAlex Richardson       double r7 = fma (a, PF[13], PF[12]);
21331914882SAlex Richardson       double r8 = fma (a, PF[15], PF[14]);
21431914882SAlex Richardson       double r9 = PF[16];
21531914882SAlex Richardson 
21631914882SAlex Richardson       double a2 = a * a;
21731914882SAlex Richardson 
21831914882SAlex Richardson       double r = r9;
21931914882SAlex Richardson       r = fma (a2, r, r8);
22031914882SAlex Richardson       r = fma (a2, r, r7);
22131914882SAlex Richardson       r = fma (a2, r, r6);
22231914882SAlex Richardson       r = fma (a2, r, r5);
22331914882SAlex Richardson       r = fma (a2, r, r4);
22431914882SAlex Richardson       r = fma (a2, r, r3);
22531914882SAlex Richardson       r = fma (a2, r, r2);
22631914882SAlex Richardson       r = fma (a2, r, r1);
22731914882SAlex Richardson 
22831914882SAlex Richardson       if (sign)
22931914882SAlex Richardson 	return -1.0 + r;
23031914882SAlex Richardson       else
23131914882SAlex Richardson 	return 1.0 - r;
23231914882SAlex Richardson     }
23331914882SAlex Richardson   else
23431914882SAlex Richardson     {
23531914882SAlex Richardson       /* Special cases : erf(nan)=nan, erf(+inf)=+1 and erf(-inf)=-1.  */
23631914882SAlex Richardson       if (unlikely (ia >= 0x7ff00000))
23731914882SAlex Richardson 	return (double) (1.0 - (sign << 1)) + 1.0 / x;
23831914882SAlex Richardson 
23931914882SAlex Richardson       if (sign)
24031914882SAlex Richardson 	return -1.0;
24131914882SAlex Richardson       else
24231914882SAlex Richardson 	return 1.0;
24331914882SAlex Richardson     }
24431914882SAlex Richardson }
245