imate
C++/CUDA Reference
special_functions.cpp
Go to the documentation of this file.
1 /*
2  * SPDX-FileCopyrightText: Copyright 2021, Siavash Ameli <sameli@berkeley.edu>
3  * SPDX-License-Identifier: BSD-3-Clause
4  * SPDX-FileType: SOURCE
5  *
6  * This program is free software: you can redistribute it and/or modify it
7  * under the terms of the license found in the LICENSE.txt file in the root
8  * directory of this source tree.
9  */
10 
11 
12 // =======
13 // Headers
14 // =======
15 
16 // Before including cmath, define _USE_MATH_DEFINES. This is only required to
17 // define the math constants like M_PI, etc, in win32 operating system.
18 #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && \
19  !defined(__CYGWIN__)
20  #define _USE_MATH_DEFINES
21 #endif
22 
23 #include <cmath> // sqrt, log, exp, erf, INFINITY, M_PI
24 #include "./special_functions.h"
25 
26 
27 // ====
28 // sign
29 // ====
30 
33 
34 double sign(const double x)
35 {
36  return (x > 0) - (x < 0);
37 }
38 
39 
40 // =======
41 // erf inv
42 // =======
43 
62 
63 double erf_inv(const double x)
64 {
65  // Output
66  double r;
67 
68  // Check extreme values
69  if ((x == 1.0) || (x == -1.0))
70  {
71  r = sign(x) * INFINITY;
72  return r;
73  }
74 
75  double a[4] = {0.886226899, -1.645349621, 0.914624893, -0.140543331};
76  double b[5] = {1.0, -2.118377725, 1.442710462, -0.329097515, 0.012229801};
77  double c[4] = {-1.970840454, -1.62490649, 3.429567803, 1.641345311};
78  double d[3] = {1.0, 3.543889200, 1.637067800};
79 
80  double z = sign(x) * x;
81 
82  if (z <= 0.7)
83  {
84  double x2 = z * z;
85  r = z * (((a[3] * x2 + a[2]) * x2 + a[1]) * x2 + a[0]);
86  r /= (((b[4] * x2 + b[3]) * x2 + b[2]) * x2 + b[1]) * x2 + b[0];
87  }
88  else
89  {
90  double y = sqrt(-log((1.0 - z) / 2.0));
91  r = (((c[3] * y + c[2]) * y + c[1]) * y + c[0]);
92  r /= ((d[2] * y + d[1]) * y + d[0]);
93  }
94 
95  r = r * sign(x);
96  z = z * sign(x);
97 
98  // These two lines below are identical and repeated for double refinement.
99  // Comment one line below for a single refinement of the Newton method.
100  r -= (erf(r) - z) / ((2.0 / sqrt(M_PI)) * exp(-r * r));
101  r -= (erf(r) - z) / ((2.0 / sqrt(M_PI)) * exp(-r * r));
102 
103  return r;
104 }
double erf_inv(const double x)
Inverse error function.
double sign(const double x)
sign function.