GeographicLib 1.52
Loading...
Searching...
No Matches
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
11
12#if defined(_MSC_VER)
13// Squelch warnings about constant conditional expressions
14# pragma warning (disable: 4127)
15#endif
16
17namespace 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
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 sq(T x)
Definition: Math.hpp:171
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)