GeographicLib 2.3

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-2022) <karney@alum.mit.edu> 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 and enum-float expressions
14# pragma warning (disable: 4127 5055)
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
43 int Math::digits10() {
44#if GEOGRAPHICLIB_PRECISION != 5
45 return numeric_limits<real>::digits10;
46#else
47 return numeric_limits<real>::digits10();
48#endif
49 }
50
51 int Math::extra_digits() {
52 return
53 digits10() > numeric_limits<double>::digits10 ?
54 digits10() - numeric_limits<double>::digits10 : 0;
55 }
56
57 template<typename T> T Math::sum(T u, T v, T& t) {
58 GEOGRAPHICLIB_VOLATILE T s = u + v;
59 GEOGRAPHICLIB_VOLATILE T up = s - v;
60 GEOGRAPHICLIB_VOLATILE T vpp = s - up;
61 up -= u;
62 vpp -= v;
63 // if s = 0, then t = 0 and give t the same sign as s
64 // mpreal needs T(0) here
65 t = s != 0 ? T(0) - (up + vpp) : s;
66 // u + v = s + t
67 // = round(u + v) + t
68 return s;
69 }
70
71 template<typename T> T Math::AngNormalize(T x) {
72 T y = remainder(x, T(td));
73#if GEOGRAPHICLIB_PRECISION == 4
74 // boost-quadmath doesn't set the sign of 0 correctly, see
75 // https://github.com/boostorg/multiprecision/issues/426
76 // Fixed by https://github.com/boostorg/multiprecision/pull/428
77 if (y == 0) y = copysign(y, x);
78#endif
79 return fabs(y) == T(hd) ? copysign(T(hd), x) : y;
80 }
81
82 template<typename T> T Math::AngDiff(T x, T y, T& e) {
83 // Use remainder instead of AngNormalize, since we treat boundary cases
84 // later taking account of the error
85 T d = sum(remainder(-x, T(td)), remainder( y, T(td)), e);
86 // This second sum can only change d if abs(d) < 128, so don't need to
87 // apply remainder yet again.
88 d = sum(remainder(d, T(td)), e, e);
89 // Fix the sign if d = -180, 0, 180.
90 if (d == 0 || fabs(d) == hd)
91 // If e == 0, take sign from y - x
92 // else (e != 0, implies d = +/-180), d and e must have opposite signs
93 d = copysign(d, e == 0 ? y - x : -e);
94 return d;
95 }
96
97 template<typename T> T Math::AngRound(T x) {
98 static const T z = T(1)/T(16);
99 GEOGRAPHICLIB_VOLATILE T y = fabs(x);
100 GEOGRAPHICLIB_VOLATILE T w = z - y;
101 // The compiler mustn't "simplify" z - (z - y) to y
102 y = w > 0 ? z - w : y;
103 return copysign(y, x);
104 }
105
106 template<typename T> void Math::sincosd(T x, T& sinx, T& cosx) {
107 // In order to minimize round-off errors, this function exactly reduces
108 // the argument to the range [-45, 45] before converting it to radians.
109 T r; int q = 0;
110 r = remquo(x, T(qd), &q); // now abs(r) <= 45
111 r *= degree<T>();
112 // g++ -O turns these two function calls into a call to sincos
113 T s = sin(r), c = cos(r);
114 switch (unsigned(q) & 3U) {
115 case 0U: sinx = s; cosx = c; break;
116 case 1U: sinx = c; cosx = -s; break;
117 case 2U: sinx = -s; cosx = -c; break;
118 default: sinx = -c; cosx = s; break; // case 3U
119 }
120 // http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1950.pdf
121 // mpreal needs T(0) here
122 cosx += T(0); // special values from F.10.1.12
123 if (sinx == 0) sinx = copysign(sinx, x); // special values from F.10.1.13
124 }
125
126 template<typename T> void Math::sincosde(T x, T t, 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 // This implementation allows x outside [-180, 180], but implementations in
130 // other languages may not.
131 T r; int q = 0;
132 r = AngRound(remquo(x, T(qd), &q) + t); // now abs(r) <= 45
133 r *= degree<T>();
134 // g++ -O turns these two function calls into a call to sincos
135 T s = sin(r), c = cos(r);
136 switch (unsigned(q) & 3U) {
137 case 0U: sinx = s; cosx = c; break;
138 case 1U: sinx = c; cosx = -s; break;
139 case 2U: sinx = -s; cosx = -c; break;
140 default: sinx = -c; cosx = s; break; // case 3U
141 }
142 // http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1950.pdf
143 // mpreal needs T(0) here
144 cosx += T(0); // special values from F.10.1.12
145 if (sinx == 0) sinx = copysign(sinx, x); // special values from F.10.1.13
146 }
147
148 template<typename T> T Math::sind(T x) {
149 // See sincosd
150 T r; int q = 0;
151 r = remquo(x, T(qd), &q); // now abs(r) <= 45
152 r *= degree<T>();
153 unsigned p = unsigned(q);
154 r = p & 1U ? cos(r) : sin(r);
155 if (p & 2U) r = -r;
156 if (r == 0) r = copysign(r, x);
157 return r;
158 }
159
160 template<typename T> T Math::cosd(T x) {
161 // See sincosd
162 T r; int q = 0;
163 r = remquo(x, T(qd), &q); // now abs(r) <= 45
164 r *= degree<T>();
165 unsigned p = unsigned(q + 1);
166 r = p & 1U ? cos(r) : sin(r);
167 if (p & 2U) r = -r;
168 // mpreal needs T(0) here
169 return T(0) + r;
170 }
171
172 template<typename T> T Math::tand(T x) {
173 static const T overflow = 1 / sq(numeric_limits<T>::epsilon());
174 T s, c;
175 sincosd(x, s, c);
176 // http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1950.pdf
177 T r = s / c; // special values from F.10.1.14
178 // With C++17 this becomes clamp(s / c, -overflow, overflow);
179 // Use max/min here (instead of fmax/fmin) to preserve NaN
180 return min(max(r, -overflow), overflow);
181 }
182
183 template<typename T> T Math::atan2d(T y, T x) {
184 // In order to minimize round-off errors, this function rearranges the
185 // arguments so that result of atan2 is in the range [-pi/4, pi/4] before
186 // converting it to degrees and mapping the result to the correct
187 // quadrant.
188 int q = 0;
189 if (fabs(y) > fabs(x)) { swap(x, y); q = 2; }
190 if (signbit(x)) { x = -x; ++q; }
191 // here x >= 0 and x >= abs(y), so angle is in [-pi/4, pi/4]
192 T ang = atan2(y, x) / degree<T>();
193 switch (q) {
194 case 1: ang = copysign(T(hd), y) - ang; break;
195 case 2: ang = qd - ang; break;
196 case 3: ang = -qd + ang; break;
197 default: break;
198 }
199 return ang;
200 }
201
202 template<typename T> T Math::atand(T x)
203 { return atan2d(x, T(1)); }
204
205 template<typename T> T Math::eatanhe(T x, T es) {
206 return es > 0 ? es * atanh(es * x) : -es * atan(es * x);
207 }
208
209 template<typename T> T Math::taupf(T tau, T es) {
210 // Need this test, otherwise tau = +/-inf gives taup = nan.
211 if (isfinite(tau)) {
212 T tau1 = hypot(T(1), tau),
213 sig = sinh( eatanhe(tau / tau1, es ) );
214 return hypot(T(1), sig) * tau - sig * tau1;
215 } else
216 return tau;
217 }
218
219 template<typename T> T Math::tauf(T taup, T es) {
220 static const int numit = 5;
221 // min iterations = 1, max iterations = 2; mean = 1.95
222 static const T tol = sqrt(numeric_limits<T>::epsilon()) / 10;
223 static const T taumax = 2 / sqrt(numeric_limits<T>::epsilon());
224 T e2m = 1 - sq(es),
225 // To lowest order in e^2, taup = (1 - e^2) * tau = _e2m * tau; so use
226 // tau = taup/e2m as a starting guess. Only 1 iteration is needed for
227 // |lat| < 3.35 deg, otherwise 2 iterations are needed. If, instead, tau
228 // = taup is used the mean number of iterations increases to 1.999 (2
229 // iterations are needed except near tau = 0).
230 //
231 // For large tau, taup = exp(-es*atanh(es)) * tau. Use this as for the
232 // initial guess for |taup| > 70 (approx |phi| > 89deg). Then for
233 // sufficiently large tau (such that sqrt(1+tau^2) = |tau|), we can exit
234 // with the intial guess and avoid overflow problems. This also reduces
235 // the mean number of iterations slightly from 1.963 to 1.954.
236 tau = fabs(taup) > 70 ? taup * exp(eatanhe(T(1), es)) : taup/e2m,
237 stol = tol * fmax(T(1), fabs(taup));
238 if (!(fabs(tau) < taumax)) return tau; // handles +/-inf and nan
239 for (int i = 0; i < numit || GEOGRAPHICLIB_PANIC; ++i) {
240 T taupa = taupf(tau, es),
241 dtau = (taup - taupa) * (1 + e2m * sq(tau)) /
242 ( e2m * hypot(T(1), tau) * hypot(T(1), taupa) );
243 tau += dtau;
244 if (!(fabs(dtau) >= stol))
245 break;
246 }
247 return tau;
248 }
249
250 template<typename T> T Math::NaN() {
251#if defined(_MSC_VER)
252 return numeric_limits<T>::has_quiet_NaN ?
253 numeric_limits<T>::quiet_NaN() :
254 (numeric_limits<T>::max)();
255#else
256 return numeric_limits<T>::has_quiet_NaN ?
257 numeric_limits<T>::quiet_NaN() :
258 numeric_limits<T>::max();
259#endif
260 }
261
262 template<typename T> T Math::infinity() {
263#if defined(_MSC_VER)
264 return numeric_limits<T>::has_infinity ?
265 numeric_limits<T>::infinity() :
266 (numeric_limits<T>::max)();
267#else
268 return numeric_limits<T>::has_infinity ?
269 numeric_limits<T>::infinity() :
270 numeric_limits<T>::max();
271#endif
272 }
273
274 /// \cond SKIP
275 // Instantiate
276#define GEOGRAPHICLIB_MATH_INSTANTIATE(T) \
277 template T GEOGRAPHICLIB_EXPORT Math::sum <T>(T, T, T&); \
278 template T GEOGRAPHICLIB_EXPORT Math::AngNormalize <T>(T); \
279 template T GEOGRAPHICLIB_EXPORT Math::AngDiff <T>(T, T, T&); \
280 template T GEOGRAPHICLIB_EXPORT Math::AngRound <T>(T); \
281 template void GEOGRAPHICLIB_EXPORT Math::sincosd <T>(T, T&, T&); \
282 template void GEOGRAPHICLIB_EXPORT Math::sincosde <T>(T, T, T&, T&); \
283 template T GEOGRAPHICLIB_EXPORT Math::sind <T>(T); \
284 template T GEOGRAPHICLIB_EXPORT Math::cosd <T>(T); \
285 template T GEOGRAPHICLIB_EXPORT Math::tand <T>(T); \
286 template T GEOGRAPHICLIB_EXPORT Math::atan2d <T>(T, T); \
287 template T GEOGRAPHICLIB_EXPORT Math::atand <T>(T); \
288 template T GEOGRAPHICLIB_EXPORT Math::eatanhe <T>(T, T); \
289 template T GEOGRAPHICLIB_EXPORT Math::taupf <T>(T, T); \
290 template T GEOGRAPHICLIB_EXPORT Math::tauf <T>(T, T); \
291 template T GEOGRAPHICLIB_EXPORT Math::NaN <T>(); \
292 template T GEOGRAPHICLIB_EXPORT Math::infinity <T>();
293
294 // Instantiate with the standard floating type
295 GEOGRAPHICLIB_MATH_INSTANTIATE(float)
296 GEOGRAPHICLIB_MATH_INSTANTIATE(double)
297#if GEOGRAPHICLIB_HAVE_LONG_DOUBLE
298 // Instantiate if long double is distinct from double
299 GEOGRAPHICLIB_MATH_INSTANTIATE(long double)
300#endif
301#if GEOGRAPHICLIB_PRECISION > 3
302 // Instantiate with the high precision type
303 GEOGRAPHICLIB_MATH_INSTANTIATE(Math::real)
304#endif
305
306#undef GEOGRAPHICLIB_MATH_INSTANTIATE
307
308 // Also we need int versions for Utility::nummatch
309 template int GEOGRAPHICLIB_EXPORT Math::NaN <int>();
310 template int GEOGRAPHICLIB_EXPORT Math::infinity<int>();
311 /// \endcond
312
313} // namespace GeographicLib
GEOGRAPHICLIB_EXPORT
#define GEOGRAPHICLIB_EXPORT
Definition: Constants.hpp:67
Math.hpp
Header for GeographicLib::Math class.
GEOGRAPHICLIB_VOLATILE
#define GEOGRAPHICLIB_VOLATILE
Definition: Math.hpp:59
GEOGRAPHICLIB_PANIC
#define GEOGRAPHICLIB_PANIC
Definition: Math.hpp:62
GEOGRAPHICLIB_PRECISION
#define GEOGRAPHICLIB_PRECISION
Definition: Math.hpp:35
GeographicLib::Math::tand
static T tand(T x)
Definition: Math.cpp:172
GeographicLib::Math::sincosd
static void sincosd(T x, T &sinx, T &cosx)
Definition: Math.cpp:106
GeographicLib::Math::atan2d
static T atan2d(T y, T x)
Definition: Math.cpp:183
GeographicLib::Math::AngRound
static T AngRound(T x)
Definition: Math.cpp:97
GeographicLib::Math::sq
static T sq(T x)
Definition: Math.hpp:205
GeographicLib::Math::sum
static T sum(T u, T v, T &t)
Definition: Math.cpp:57
GeographicLib::Math::sind
static T sind(T x)
Definition: Math.cpp:148
GeographicLib::Math::tauf
static T tauf(T taup, T es)
Definition: Math.cpp:219
GeographicLib::Math::AngNormalize
static T AngNormalize(T x)
Definition: Math.cpp:71
GeographicLib::Math::digits10
static int digits10()
Definition: Math.cpp:43
GeographicLib::Math::atand
static T atand(T x)
Definition: Math.cpp:202
GeographicLib::Math::digits
static int digits()
Definition: Math.cpp:26
GeographicLib::Math::infinity
static T infinity()
Definition: Math.cpp:262
GeographicLib::Math::sincosde
static void sincosde(T x, T t, T &sinx, T &cosx)
Definition: Math.cpp:126
GeographicLib::Math::taupf
static T taupf(T tau, T es)
Definition: Math.cpp:209
GeographicLib::Math::NaN
static T NaN()
Definition: Math.cpp:250
GeographicLib::Math::AngDiff
static T AngDiff(T x, T y, T &e)
Definition: Math.cpp:82
GeographicLib::Math::eatanhe
static T eatanhe(T x, T es)
Definition: Math.cpp:205
GeographicLib::Math::set_digits
static int set_digits(int ndigits)
Definition: Math.cpp:34
GeographicLib::Math::real
double real
Definition: Math.hpp:100
GeographicLib::Math::cosd
static T cosd(T x)
Definition: Math.cpp:160
GeographicLib::Math::td
@ td
degrees per turn
Definition: Math.hpp:142
GeographicLib::Math::hd
@ hd
degrees per half turn
Definition: Math.hpp:141
GeographicLib::Math::qd
@ qd
degrees per quarter turn
Definition: Math.hpp:138
GeographicLib::Math::extra_digits
static int extra_digits()
Definition: Math.cpp:51
GeographicLib
Namespace for GeographicLib.
Definition: Accumulator.cpp:12
std
Definition: NearestNeighbor.hpp:812
std::swap
void swap(GeographicLib::NearestNeighbor< dist_t, pos_t, distfun_t > &a, GeographicLib::NearestNeighbor< dist_t, pos_t, distfun_t > &b)
Definition: NearestNeighbor.hpp:825