NEURON
math.cpp
Go to the documentation of this file.
1 #include "oc_ansi.h"
2 #ifndef __INTEL_LLVM_COMPILER
3 #ifdef __clang__
4 #pragma float_control(precise, on)
5 #endif
6 #pragma STDC FENV_ACCESS ON
7 #endif
8 
9 #include <../../nrnconf.h>
10 /* a fake change */
11 /* /local/src/master/nrn/src/oc/math.cpp,v 1.6 1999/07/16 13:43:10 hines Exp */
12 
13 #include "nrnmpiuse.h"
14 #include "ocfunc.h"
15 #include "nrnassrt.h"
16 
17 #include <cfenv>
18 #include <cmath>
19 #include <cerrno>
20 #include <cstdio>
21 
22 #if NRN_ENABLE_ARCH_INDEP_EXP_POW
23 #include <mpfr.h>
24 #endif
25 
26 #define EPS hoc_epsilon
27 #define MAXERRCOUNT 5
28 
30 
31 #ifdef MINGW
32 static const auto errno_enabled = true;
33 static const auto check_fe_except = false;
34 #elif defined(NRN_CHECK_FE_EXCEPTIONS)
35 static constexpr auto errno_enabled = false;
36 static constexpr auto check_fe_except = true;
37 #ifdef math_errhandling
38 // LLVM-based Intel compiles don't define math_errhandling when -fp-model=fast
39 static_assert(math_errhandling & MATH_ERREXCEPT);
40 #endif
41 #else
42 static const auto errno_enabled = math_errhandling & MATH_ERRNO;
43 static const auto check_fe_except = !errno_enabled && math_errhandling & MATH_ERREXCEPT;
44 #endif
45 
46 static inline void clear_fe_except() {
47  if (check_fe_except) {
48  std::feclearexcept(FE_ALL_EXCEPT);
49  }
50 }
51 
52 static double errcheck(double, const char*);
53 
54 void hoc_atan2(void) {
55  double d;
56  d = atan2(*hoc_getarg(1), *hoc_getarg(2));
57  hoc_ret();
58  hoc_pushx(d);
59 }
60 
61 double hoc_Log(double x) {
63  return errcheck(log(x), "log");
64 }
65 
66 double hoc_Log10(double x) {
68  return errcheck(log10(x), "log10");
69 }
70 
71 #if NRN_ENABLE_ARCH_INDEP_EXP_POW
72 
73 static double accuracy32(double val) {
74  int ex;
75  double mant = frexp(val, &ex);
76  // round to about 32 bits after .
77  double prec = 4294967296.0;
78  double result = mant * prec;
79  result = round(result);
80  result /= prec;
81  result = ldexp(result, ex);
82  return result;
83 }
84 
85 static double pow_arch_indep(double x, double y) {
86  mpfr_prec_t prec = 53;
87  mpfr_rnd_t rnd = MPFR_RNDN;
88 
89  mpfr_t x_, y_;
90  mpfr_init2(x_, prec);
91  mpfr_init2(y_, prec);
92 
93  mpfr_set_d(x_, x, rnd);
94  mpfr_set_d(y_, y, rnd);
95 
96  mpfr_pow(x_, x_, y_, rnd);
97 
98  double r = mpfr_get_d(x_, rnd);
99 
100  mpfr_clear(x_);
101  mpfr_clear(y_);
102 
103  return r;
104 }
105 
106 static double exp_arch_indep(double x) {
107  mpfr_prec_t prec = 53;
108  mpfr_rnd_t rnd = MPFR_RNDN;
109 
110  mpfr_t x_;
111  mpfr_init2(x_, prec);
112  mpfr_set_d(x_, x, rnd);
113  mpfr_exp(x_, x_, rnd);
114  double r = mpfr_get_d(x_, rnd);
115  mpfr_clear(x_);
116  return r;
117 }
118 
119 static double pow_precision32(double x, double y) {
120  return accuracy32(pow(x, y));
121 }
122 
123 static double exp_precision32(double x) {
124  return accuracy32(exp(x));
125 }
126 
127 static double (*pow_ptr)(double x, double y) = pow;
128 static double (*pow_ieee_ptr)(double x,
129  double y) = pow; // avoid error! Maybe because of c++ overloading.
130 static double (*exp_ptr)(double x) = exp;
131 
132 int nrn_use_exp_pow_precision(int style) {
133  if (style == 0) { // default IEEE
134  pow_ptr = pow;
135  exp_ptr = exp;
136  } else if (style == 1) { // 53 bit mpfr
137  pow_ptr = pow_arch_indep;
138  exp_ptr = exp_arch_indep;
139  } else if (style == 2) { // 32 bit truncation
140  pow_ptr = pow_precision32;
141  exp_ptr = exp_precision32;
142  }
143  return style;
144 }
145 
146 #endif // NRN_ENABLE_ARCH_INDEP_EXP_POW
147 
149  int style = 0;
150  if (ifarg(1)) {
151  style = chkarg(1, 0.0, 2.0);
152  }
153 #if NRN_ENABLE_ARCH_INDEP_EXP_POW
154  style = nrn_use_exp_pow_precision(style);
155 #else
156  style = 0;
157 #endif // NRN_ENABLE_ARCH_INDEP_EXP_POW
158  hoc_ret();
159  hoc_pushx(double(style));
160 }
161 
162 // Try to overcome difference between linux and windows.
163 // by rounding mantissa to 32 bit accuracy or using
164 // hopefully arch independent mpfr for exp and pow.
165 
166 double hoc_pow(double x, double y) {
167 #if NRN_ENABLE_ARCH_INDEP_EXP_POW
168  return (*pow_ptr)(x, y);
169 #else
170  return pow(x, y);
171 #endif
172 }
173 
174 /* used by nmodl and other c, c++ code */
175 double hoc_Exp(double x) {
176  if (x < -700.) {
177  return 0.;
178  } else if (x > 700 && nrn_feenableexcept_ == 0) {
179  errno = ERANGE;
180  if (check_fe_except) {
181  std::feraiseexcept(FE_OVERFLOW);
182  }
183  if (++hoc_errno_count < MAXERRCOUNT) {
184  fprintf(stderr, "exp(%g) out of range, returning exp(700)\n", x);
185  }
186  if (hoc_errno_count == MAXERRCOUNT) {
187  fprintf(stderr, "No more errno warnings during this execution\n");
188  }
189  return exp(700.);
190  }
191 
192 #if NRN_ENABLE_ARCH_INDEP_EXP_POW
193  return (*exp_ptr)(x);
194 #else
195  return exp(x);
196 #endif
197 }
198 
199 /* used by interpreter */
200 double hoc1_Exp(double x) {
201  if (x < -700.) {
202  return 0.;
203  } else if (x > 700) {
204  errno = ERANGE;
205  if (check_fe_except) {
206  std::feraiseexcept(FE_OVERFLOW);
207  }
208  return errcheck(exp(700.), "exp");
209  }
210  clear_fe_except();
211  return errcheck(exp(x), "exp");
212 }
213 
214 double hoc_Sqrt(double x) {
215  clear_fe_except();
216  return errcheck(sqrt(x), "sqrt");
217 }
218 
219 double hoc_Pow(double x, double y) {
220  clear_fe_except();
221  return errcheck(hoc_pow(x, y), "exponentiation");
222 }
223 
224 double hoc_integer(double x) {
225  if (x < 0) {
226  return (double) (long) (x - EPS);
227  } else {
228  return (double) (long) (x + EPS);
229  }
230 }
231 
232 double errcheck(double d, const char* s) /* check result of library call */
233 {
234  // errno may not be enabled, rely on FE exceptions in that case. See:
235  // https://en.cppreference.com/w/cpp/numeric/math/math_errhandling
236  if ((errno_enabled && errno == EDOM) || (check_fe_except && std::fetestexcept(FE_INVALID))) {
237  clear_fe_except();
238  errno = 0;
239  hoc_execerror(s, "argument out of domain");
240  } else if ((errno_enabled && errno == ERANGE) ||
241  (check_fe_except &&
242  (std::fetestexcept(FE_DIVBYZERO) || std::fetestexcept(FE_OVERFLOW) ||
243  std::fetestexcept(FE_UNDERFLOW)))) {
244  clear_fe_except();
245  errno = 0;
246  if (++hoc_errno_count > MAXERRCOUNT) {
247  } else {
248  hoc_warning(s, "result out of range");
249  if (hoc_errno_count == MAXERRCOUNT) {
250  fprintf(stderr, "No more errno warnings during this execution\n");
251  }
252  }
253  }
254  return d;
255 }
256 
257 int hoc_errno_check(void) {
258  errno = 0;
259  return 0;
260 }
#define y_(arg)
Crout matrix decomposition : Forward/Backward substitution.
Definition: crout.hpp:136
double chkarg(int, double low, double high)
Definition: code2.cpp:626
double * hoc_getarg(int narg)
Definition: code.cpp:1641
void hoc_ret()
int hoc_errno_check(void)
Definition: math.cpp:257
double hoc_Exp(double x)
Definition: math.cpp:175
double hoc_pow(double x, double y)
Definition: math.cpp:166
int nrn_feenableexcept_
Definition: hoc.cpp:82
void hoc_pushx(double)
Definition: code.cpp:779
static const auto check_fe_except
Definition: math.cpp:43
void hoc_use_exp_pow_precision()
Definition: math.cpp:148
static const auto errno_enabled
Definition: math.cpp:42
static double errcheck(double, const char *)
Definition: math.cpp:232
double hoc_Sqrt(double x)
Definition: math.cpp:214
static void clear_fe_except()
Definition: math.cpp:46
#define EPS
Definition: math.cpp:26
int hoc_errno_count
Definition: math.cpp:29
double hoc_Log10(double x)
Definition: math.cpp:66
double hoc1_Exp(double x)
Definition: math.cpp:200
#define MAXERRCOUNT
Definition: math.cpp:27
double hoc_Log(double x)
Definition: math.cpp:61
double hoc_integer(double x)
Definition: math.cpp:224
double hoc_Pow(double x, double y)
Definition: math.cpp:219
void hoc_atan2(void)
Definition: math.cpp:54
sqrt
Definition: extdef.h:3
log10
Definition: extdef.h:4
atan2
Definition: extdef.h:4
exp
Definition: extdef.h:5
pow
Definition: extdef.h:4
log
Definition: extdef.h:4
void hoc_execerror(const char *s1, const char *s2)
Definition: nrnoc_aux.cpp:39
void hoc_warning(const char *s1, const char *s2)
Definition: nrnoc_aux.cpp:44
s
Definition: multisend.cpp:521
int ifarg(int)
Definition: code.cpp:1607
static N_Vector x_
HOC interpreter function declarations (included by hocdec.h)