GeographicLib  1.52
Math.cpp
Go to the documentation of this file.
1 /**
2  * \file Math.cpp
3  * \brief Implementation for GeographicLib::Math class
4  *
5  * Copyright (c) Charles Karney (2015-2021) <charles@karney.com> and licensed
6  * under the MIT/X11 License. For more information, see
7  * https://geographiclib.sourceforge.io/
8  **********************************************************************/
9 
10 #include <GeographicLib/Math.hpp>
11 
12 #if defined(_MSC_VER)
13 // Squelch warnings about constant conditional expressions
14 # pragma warning (disable: 4127)
15 #endif
16 
17 namespace GeographicLib {
18 
19  using namespace std;
20 
21  void Math::dummy() {
22  static_assert(GEOGRAPHICLIB_PRECISION >= 1 && GEOGRAPHICLIB_PRECISION <= 5,
23  "Bad value of precision");
24  }
25 
26  int Math::digits() {
27 #if GEOGRAPHICLIB_PRECISION != 5
28  return numeric_limits<real>::digits;
29 #else
30  return numeric_limits<real>::digits();
31 #endif
32  }
33 
34  int Math::set_digits(int ndigits) {
35 #if GEOGRAPHICLIB_PRECISION != 5
36  (void)ndigits;
37 #else
38  mpfr::mpreal::set_default_prec(ndigits >= 2 ? ndigits : 2);
39 #endif
40  return digits();
41  }
42 
44 #if GEOGRAPHICLIB_PRECISION != 5
45  return numeric_limits<real>::digits10;
46 #else
47  return numeric_limits<real>::digits10();
48 #endif
49  }
50 
52  return
53  digits10() > numeric_limits<double>::digits10 ?
54  digits10() - numeric_limits<double>::digits10 : 0;
55  }
56 
57  template<typename T> T Math::hypot(T x, T y) {
58  using std::hypot; return hypot(x, y);
59  }
60 
61  template<typename T> T Math::expm1(T x) {
62  using std::expm1; return expm1(x);
63  }
64 
65  template<typename T> T Math::log1p(T x) {
66  using std::log1p; return log1p(x);
67  }
68 
69  template<typename T> T Math::asinh(T x) {
70  using std::asinh; return asinh(x);
71  }
72 
73  template<typename T> T Math::atanh(T x) {
74  using std::atanh; return atanh(x);
75  }
76 
77  template<typename T> T Math::copysign(T x, T y) {
78  using std::copysign; return copysign(x, y);
79  }
80 
81  template<typename T> T Math::cbrt(T x) {
82  using std::cbrt; return cbrt(x);
83  }
84 
85  template<typename T> T Math::remainder(T x, T y) {
86  using std::remainder; return remainder(x, y);
87  }
88 
89  template<typename T> T Math::remquo(T x, T y, int* n) {
90  using std::remquo; return remquo(x, y, n);
91  }
92 
93  template<typename T> T Math::round(T x) {
94  using std::round; return round(x);
95  }
96 
97  template<typename T> long Math::lround(T x) {
98  using std::lround; return lround(x);
99  }
100 
101  template<typename T> T Math::fma(T x, T y, T z) {
102  using std::fma; return fma(x, y, z);
103  }
104 
105  template<typename T> T Math::sum(T u, T v, T& t) {
106  GEOGRAPHICLIB_VOLATILE T s = u + v;
107  GEOGRAPHICLIB_VOLATILE T up = s - v;
108  GEOGRAPHICLIB_VOLATILE T vpp = s - up;
109  up -= u;
110  vpp -= v;
111  t = -(up + vpp);
112  // u + v = s + t
113  // = round(u + v) + t
114  return s;
115  }
116 
117  template<typename T> T Math::AngRound(T x) {
118  static const T z = 1/T(16);
119  if (x == 0) return 0;
120  GEOGRAPHICLIB_VOLATILE T y = abs(x);
121  // The compiler mustn't "simplify" z - (z - y) to y
122  y = y < z ? z - (z - y) : y;
123  return x < 0 ? -y : y;
124  }
125 
126  template<typename T> void Math::sincosd(T x, T& sinx, T& cosx) {
127  // In order to minimize round-off errors, this function exactly reduces
128  // the argument to the range [-45, 45] before converting it to radians.
129  using std::remquo;
130  T r; int q = 0;
131  // N.B. the implementation of remquo in glibc pre 2.22 were buggy. See
132  // https://sourceware.org/bugzilla/show_bug.cgi?id=17569
133  // This was fixed in version 2.22 on 2015-08-05
134  r = remquo(x, T(90), &q); // now abs(r) <= 45
135  r *= degree<T>();
136  // g++ -O turns these two function calls into a call to sincos
137  T s = sin(r), c = cos(r);
138  switch (unsigned(q) & 3U) {
139  case 0U: sinx = s; cosx = c; break;
140  case 1U: sinx = c; cosx = -s; break;
141  case 2U: sinx = -s; cosx = -c; break;
142  default: sinx = -c; cosx = s; break; // case 3U
143  }
144  // Set sign of 0 results. -0 only produced for sin(-0)
145  if (x != 0) { sinx += T(0); cosx += T(0); }
146  }
147 
148  template<typename T> T Math::sind(T x) {
149  // See sincosd
150  using std::remquo;
151  T r; int q = 0;
152  r = remquo(x, T(90), &q); // now abs(r) <= 45
153  r *= degree<T>();
154  unsigned p = unsigned(q);
155  r = p & 1U ? cos(r) : sin(r);
156  if (p & 2U) r = -r;
157  if (x != 0) r += T(0);
158  return r;
159  }
160 
161  template<typename T> T Math::cosd(T x) {
162  // See sincosd
163  using std::remquo;
164  T r; int q = 0;
165  r = remquo(x, T(90), &q); // now abs(r) <= 45
166  r *= degree<T>();
167  unsigned p = unsigned(q + 1);
168  r = p & 1U ? cos(r) : sin(r);
169  if (p & 2U) r = -r;
170  return T(0) + r;
171  }
172 
173  template<typename T> T Math::tand(T x) {
174  static const T overflow = 1 / sq(numeric_limits<T>::epsilon());
175  T s, c;
176  sincosd(x, s, c);
177  return c != 0 ? s / c : (s < 0 ? -overflow : overflow);
178  }
179 
180  template<typename T> T Math::atan2d(T y, T x) {
181  // In order to minimize round-off errors, this function rearranges the
182  // arguments so that result of atan2 is in the range [-pi/4, pi/4] before
183  // converting it to degrees and mapping the result to the correct
184  // quadrant.
185  int q = 0;
186  if (abs(y) > abs(x)) { swap(x, y); q = 2; }
187  if (x < 0) { x = -x; ++q; }
188  // here x >= 0 and x >= abs(y), so angle is in [-pi/4, pi/4]
189  T ang = atan2(y, x) / degree<T>();
190  switch (q) {
191  // Note that atan2d(-0.0, 1.0) will return -0. However, we expect that
192  // atan2d will not be called with y = -0. If need be, include
193  //
194  // case 0: ang = 0 + ang; break;
195  //
196  // and handle mpfr as in AngRound.
197  case 1: ang = (y >= 0 ? 180 : -180) - ang; break;
198  case 2: ang = 90 - ang; break;
199  case 3: ang = -90 + ang; break;
200  default: break;
201  }
202  return ang;
203  }
204 
205  template<typename T> T Math::atand(T x)
206  { return atan2d(x, T(1)); }
207 
208  template<typename T> T Math::eatanhe(T x, T es) {
209  using std::atanh;
210  return es > T(0) ? es * atanh(es * x) : -es * atan(es * x);
211  }
212 
213  template<typename T> T Math::taupf(T tau, T es) {
214  // Need this test, otherwise tau = +/-inf gives taup = nan.
215  using std::isfinite; using std::hypot;
216  if (isfinite(tau)) {
217  T tau1 = hypot(T(1), tau),
218  sig = sinh( eatanhe(tau / tau1, es ) );
219  return hypot(T(1), sig) * tau - sig * tau1;
220  } else
221  return tau;
222  }
223 
224  template<typename T> T Math::tauf(T taup, T es) {
225  using std::hypot;
226  static const int numit = 5;
227  // min iterations = 1, max iterations = 2; mean = 1.95
228  static const T tol = sqrt(numeric_limits<T>::epsilon()) / 10;
229  static const T taumax = 2 / sqrt(numeric_limits<T>::epsilon());
230  T e2m = T(1) - sq(es),
231  // To lowest order in e^2, taup = (1 - e^2) * tau = _e2m * tau; so use
232  // tau = taup/e2m as a starting guess. Only 1 iteration is needed for
233  // |lat| < 3.35 deg, otherwise 2 iterations are needed. If, instead, tau
234  // = taup is used the mean number of iterations increases to 1.999 (2
235  // iterations are needed except near tau = 0).
236  //
237  // For large tau, taup = exp(-es*atanh(es)) * tau. Use this as for the
238  // initial guess for |taup| > 70 (approx |phi| > 89deg). Then for
239  // sufficiently large tau (such that sqrt(1+tau^2) = |tau|), we can exit
240  // with the intial guess and avoid overflow problems. This also reduces
241  // the mean number of iterations slightly from 1.963 to 1.954.
242  tau = abs(taup) > 70 ? taup * exp(eatanhe(T(1), es)) : taup/e2m,
243  stol = tol * max(T(1), abs(taup));
244  if (!(abs(tau) < taumax)) return tau; // handles +/-inf and nan
245  for (int i = 0; i < numit || GEOGRAPHICLIB_PANIC; ++i) {
246  T taupa = taupf(tau, es),
247  dtau = (taup - taupa) * (1 + e2m * sq(tau)) /
248  ( e2m * hypot(T(1), tau) * hypot(T(1), taupa) );
249  tau += dtau;
250  if (!(abs(dtau) >= stol))
251  break;
252  }
253  return tau;
254  }
255 
256  template<typename T> bool Math::isfinite(T x) {
257  using std::isfinite; return isfinite(x);
258  }
259 
260  template<typename T> T Math::NaN() {
261 #if defined(_MSC_VER)
262  return numeric_limits<T>::has_quiet_NaN ?
263  numeric_limits<T>::quiet_NaN() :
264  (numeric_limits<T>::max)();
265 #else
266  return numeric_limits<T>::has_quiet_NaN ?
267  numeric_limits<T>::quiet_NaN() :
268  numeric_limits<T>::max();
269 #endif
270  }
271 
272  template<typename T> bool Math::isnan(T x) {
273  using std::isnan; return isnan(x);
274  }
275 
276  template<typename T> T Math::infinity() {
277 #if defined(_MSC_VER)
278  return numeric_limits<T>::has_infinity ?
279  numeric_limits<T>::infinity() :
280  (numeric_limits<T>::max)();
281 #else
282  return numeric_limits<T>::has_infinity ?
283  numeric_limits<T>::infinity() :
284  numeric_limits<T>::max();
285 #endif
286  }
287 
288  /// \cond SKIP
289  // Instantiate
290 #define GEOGRAPHICLIB_MATH_INSTANTIATE(T) \
291  template T GEOGRAPHICLIB_EXPORT Math::hypot <T>(T, T); \
292  template T GEOGRAPHICLIB_EXPORT Math::expm1 <T>(T); \
293  template T GEOGRAPHICLIB_EXPORT Math::log1p <T>(T); \
294  template T GEOGRAPHICLIB_EXPORT Math::asinh <T>(T); \
295  template T GEOGRAPHICLIB_EXPORT Math::atanh <T>(T); \
296  template T GEOGRAPHICLIB_EXPORT Math::cbrt <T>(T); \
297  template T GEOGRAPHICLIB_EXPORT Math::remainder<T>(T, T); \
298  template T GEOGRAPHICLIB_EXPORT Math::remquo <T>(T, T, int*); \
299  template T GEOGRAPHICLIB_EXPORT Math::round <T>(T); \
300  template long GEOGRAPHICLIB_EXPORT Math::lround <T>(T); \
301  template T GEOGRAPHICLIB_EXPORT Math::copysign <T>(T, T); \
302  template T GEOGRAPHICLIB_EXPORT Math::fma <T>(T, T, T); \
303  template T GEOGRAPHICLIB_EXPORT Math::sum <T>(T, T, T&); \
304  template T GEOGRAPHICLIB_EXPORT Math::AngRound <T>(T); \
305  template void GEOGRAPHICLIB_EXPORT Math::sincosd <T>(T, T&, T&); \
306  template T GEOGRAPHICLIB_EXPORT Math::sind <T>(T); \
307  template T GEOGRAPHICLIB_EXPORT Math::cosd <T>(T); \
308  template T GEOGRAPHICLIB_EXPORT Math::tand <T>(T); \
309  template T GEOGRAPHICLIB_EXPORT Math::atan2d <T>(T, T); \
310  template T GEOGRAPHICLIB_EXPORT Math::atand <T>(T); \
311  template T GEOGRAPHICLIB_EXPORT Math::eatanhe <T>(T, T); \
312  template T GEOGRAPHICLIB_EXPORT Math::taupf <T>(T, T); \
313  template T GEOGRAPHICLIB_EXPORT Math::tauf <T>(T, T); \
314  template bool GEOGRAPHICLIB_EXPORT Math::isfinite <T>(T); \
315  template T GEOGRAPHICLIB_EXPORT Math::NaN <T>(); \
316  template bool GEOGRAPHICLIB_EXPORT Math::isnan <T>(T); \
317  template T GEOGRAPHICLIB_EXPORT Math::infinity <T>();
318 
319  // Instantiate with the standard floating type
320  GEOGRAPHICLIB_MATH_INSTANTIATE(float)
321  GEOGRAPHICLIB_MATH_INSTANTIATE(double)
322 #if GEOGRAPHICLIB_HAVE_LONG_DOUBLE
323  // Instantiate if long double is distinct from double
324  GEOGRAPHICLIB_MATH_INSTANTIATE(long double)
325 #endif
326 #if GEOGRAPHICLIB_PRECISION > 3
327  // Instantiate with the high precision type
328  GEOGRAPHICLIB_MATH_INSTANTIATE(Math::real)
329 #endif
330 
331 #undef GEOGRAPHICLIB_MATH_INSTANTIATE
332 
333  // Also we need int versions for Utility::nummatch
334  template int GEOGRAPHICLIB_EXPORT Math::NaN <int>();
335  template int GEOGRAPHICLIB_EXPORT Math::infinity<int>();
336  /// \endcond
337 
338 } // namespace GeographicLib
#define GEOGRAPHICLIB_EXPORT
Definition: Constants.hpp:66
Header for GeographicLib::Math class.
#define GEOGRAPHICLIB_VOLATILE
Definition: Math.hpp:58
#define GEOGRAPHICLIB_PANIC
Definition: Math.hpp:61
#define GEOGRAPHICLIB_PRECISION
Definition: Math.hpp:35
static T remquo(T x, T y, int *n)
Definition: Math.cpp:89
static T tand(T x)
Definition: Math.cpp:173
static T cbrt(T x)
Definition: Math.cpp:81
static void sincosd(T x, T &sinx, T &cosx)
Definition: Math.cpp:126
static T atan2d(T y, T x)
Definition: Math.cpp:180
static T expm1(T x)
Definition: Math.cpp:61
static T log1p(T x)
Definition: Math.cpp:65
static T fma(T x, T y, T z)
Definition: Math.cpp:101
static T AngRound(T x)
Definition: Math.cpp:117
static T copysign(T x, T y)
Definition: Math.cpp:77
static T round(T x)
Definition: Math.cpp:93
static T sum(T u, T v, T &t)
Definition: Math.cpp:105
static T sind(T x)
Definition: Math.cpp:148
static bool isnan(T x)
Definition: Math.cpp:272
static T tauf(T taup, T es)
Definition: Math.cpp:224
static int digits10()
Definition: Math.cpp:43
static T atand(T x)
Definition: Math.cpp:205
static long lround(T x)
Definition: Math.cpp:97
static T atanh(T x)
Definition: Math.cpp:73
static int digits()
Definition: Math.cpp:26
static T infinity()
Definition: Math.cpp:276
static T asinh(T x)
Definition: Math.cpp:69
static T hypot(T x, T y)
Definition: Math.cpp:57
static bool isfinite(T x)
Definition: Math.cpp:256
static T taupf(T tau, T es)
Definition: Math.cpp:213
static T NaN()
Definition: Math.cpp:260
static T remainder(T x, T y)
Definition: Math.cpp:85
static T eatanhe(T x, T es)
Definition: Math.cpp:208
static int set_digits(int ndigits)
Definition: Math.cpp:34
static T cosd(T x)
Definition: Math.cpp:161
static int extra_digits()
Definition: Math.cpp:51
Namespace for GeographicLib.
Definition: Accumulator.cpp:12
void swap(GeographicLib::NearestNeighbor< dist_t, pos_t, distfun_t > &a, GeographicLib::NearestNeighbor< dist_t, pos_t, distfun_t > &b)