GeographicLib 2.3

Math.hpp
Go to the documentation of this file.
1/**
2 * \file Math.hpp
3 * \brief Header for GeographicLib::Math class
4 *
5 * Copyright (c) Charles Karney (2008-2023) <karney@alum.mit.edu> and licensed
6 * under the MIT/X11 License. For more information, see
7 * https://geographiclib.sourceforge.io/
8 **********************************************************************/
9
10// Constants.hpp includes Math.hpp. Place this include outside Math.hpp's
11// include guard to enforce this ordering.
12#include <GeographicLib/Constants.hpp>
13
14#if !defined(GEOGRAPHICLIB_MATH_HPP)
15#define GEOGRAPHICLIB_MATH_HPP 1
16
17#if !defined(GEOGRAPHICLIB_WORDS_BIGENDIAN)
18# define GEOGRAPHICLIB_WORDS_BIGENDIAN 0
19#endif
20
21#if !defined(GEOGRAPHICLIB_HAVE_LONG_DOUBLE)
22# define GEOGRAPHICLIB_HAVE_LONG_DOUBLE 0
23#endif
24
25#if !defined(GEOGRAPHICLIB_PRECISION)
26/**
27 * The precision of floating point numbers used in %GeographicLib. 1 means
28 * float (single precision); 2 (the default) means double; 3 means long double;
29 * 4 is reserved for quadruple precision. Nearly all the testing has been
30 * carried out with doubles and that's the recommended configuration. In order
31 * for long double to be used, GEOGRAPHICLIB_HAVE_LONG_DOUBLE needs to be
32 * defined. Note that with Microsoft Visual Studio, long double is the same as
33 * double.
34 **********************************************************************/
35# define GEOGRAPHICLIB_PRECISION 2
36#endif
37
38#include <cmath>
39#include <algorithm>
40#include <limits>
41
42#if GEOGRAPHICLIB_PRECISION == 4
43#include <memory>
44#include <boost/version.hpp>
45#include <boost/multiprecision/float128.hpp>
46#include <boost/math/special_functions.hpp>
47#elif GEOGRAPHICLIB_PRECISION == 5
48#include <mpreal.h>
49#endif
50
51#if GEOGRAPHICLIB_PRECISION > 3
52// volatile keyword makes no sense for multiprec types
53#define GEOGRAPHICLIB_VOLATILE
54// Signal a convergence failure with multiprec types by throwing an exception
55// at loop exit.
56#define GEOGRAPHICLIB_PANIC \
57 (throw GeographicLib::GeographicErr("Convergence failure"), false)
58#else
59#define GEOGRAPHICLIB_VOLATILE volatile
60// Ignore convergence failures with standard floating points types by allowing
61// loop to exit cleanly.
62#define GEOGRAPHICLIB_PANIC false
63#endif
64
65 namespace GeographicLib {
66
67 /**
68 * \brief Mathematical functions needed by %GeographicLib
69 *
70 * Define mathematical functions in order to localize system dependencies and
71 * to provide generic versions of the functions. In addition define a real
72 * type to be used by %GeographicLib.
73 *
74 * Example of use:
75 * \include example-Math.cpp
76 **********************************************************************/
77 class GEOGRAPHICLIB_EXPORT Math {
78 private:
79 void dummy(); // Static check for GEOGRAPHICLIB_PRECISION
80 Math() = delete; // Disable constructor
81 public:
82
83#if GEOGRAPHICLIB_HAVE_LONG_DOUBLE
84 /**
85 * The extended precision type for real numbers, used for some testing.
86 * This is long double on computers with this type; otherwise it is double.
87 **********************************************************************/
88 typedef long double extended;
89#else
90 typedef double extended;
91#endif
92
93#if GEOGRAPHICLIB_PRECISION == 2
94 /**
95 * The real type for %GeographicLib. Nearly all the testing has been done
96 * with \e real = double. However, the algorithms should also work with
97 * float and long double (where available). (<b>CAUTION</b>: reasonable
98 * accuracy typically cannot be obtained using floats.)
99 **********************************************************************/
100 typedef double real;
101#elif GEOGRAPHICLIB_PRECISION == 1
102 typedef float real;
103#elif GEOGRAPHICLIB_PRECISION == 3
104 typedef extended real;
105#elif GEOGRAPHICLIB_PRECISION == 4
106 typedef boost::multiprecision::float128 real;
107#elif GEOGRAPHICLIB_PRECISION == 5
108 typedef mpfr::mpreal real;
109#else
110 typedef double real;
111#endif
112
113 /**
114 * The constants defining the standard (Babylonian) meanings of degrees,
115 * minutes, and seconds, for angles. Read the constants as follows (for
116 * example): \e ms = 60 is the ratio 1 minute / 1 second. The
117 * abbreviations are
118 * - \e t a whole turn (360&deg;)
119 * - \e h a half turn (180&deg;)
120 * - \e q a quarter turn (a right angle = 90&deg;)
121 * - \e d a degree
122 * - \e m a minute
123 * - \e s a second
124 * .
125 * Note that degree() is ratio 1 degree / 1 radian, thus, for example,
126 * Math::degree() * Math::qd is the ratio 1 quarter turn / 1 radian =
127 * &pi;/2.
128 *
129 * Defining all these in one place would mean that it's simple to convert
130 * to the centesimal system for measuring angles. The DMS class assumes
131 * that Math::dm and Math::ms are less than or equal to 100 (so that two
132 * digits suffice for the integer parts of the minutes and degrees
133 * components of an angle). Switching to the centesimal convention will
134 * break most of the tests. Also the normal definition of degree is baked
135 * into some classes, e.g., UTMUPS, MGRS, Georef, Geohash, etc.
136 **********************************************************************/
137 enum dms {
138 qd = 90, ///< degrees per quarter turn
139 dm = 60, ///< minutes per degree
140 ms = 60, ///< seconds per minute
141 hd = 2 * qd, ///< degrees per half turn
142 td = 2 * hd, ///< degrees per turn
143 ds = dm * ms ///< seconds per degree
144 };
145
146 /**
147 * @return the number of bits of precision in a real number.
148 **********************************************************************/
149 static int digits();
150
151 /**
152 * Set the binary precision of a real number.
153 *
154 * @param[in] ndigits the number of bits of precision.
155 * @return the resulting number of bits of precision.
156 *
157 * This only has an effect when GEOGRAPHICLIB_PRECISION = 5. See also
158 * Utility::set_digits for caveats about when this routine should be
159 * called.
160 **********************************************************************/
161 static int set_digits(int ndigits);
162
163 /**
164 * @return the number of decimal digits of precision in a real number.
165 **********************************************************************/
166 static int digits10();
167
168 /**
169 * Number of additional decimal digits of precision for real relative to
170 * double (0 for float).
171 **********************************************************************/
172 static int extra_digits();
173
174 /**
175 * true if the machine is big-endian.
176 **********************************************************************/
177 static const bool bigendian = GEOGRAPHICLIB_WORDS_BIGENDIAN;
178
179 /**
180 * @tparam T the type of the returned value.
181 * @return &pi;.
182 **********************************************************************/
183 template<typename T = real> static T pi() {
184 using std::atan2;
185 static const T pi = atan2(T(0), T(-1));
186 return pi;
187 }
188
189 /**
190 * @tparam T the type of the returned value.
191 * @return the number of radians in a degree.
192 **********************************************************************/
193 template<typename T = real> static T degree() {
194 static const T degree = pi<T>() / T(hd);
195 return degree;
196 }
197
198 /**
199 * Square a number.
200 *
201 * @tparam T the type of the argument and the returned value.
202 * @param[in] x
203 * @return <i>x</i><sup>2</sup>.
204 **********************************************************************/
205 template<typename T> static T sq(T x)
206 { return x * x; }
207
208 /**
209 * Normalize a two-vector.
210 *
211 * @tparam T the type of the argument and the returned value.
212 * @param[in,out] x on output set to <i>x</i>/hypot(<i>x</i>, <i>y</i>).
213 * @param[in,out] y on output set to <i>y</i>/hypot(<i>x</i>, <i>y</i>).
214 **********************************************************************/
215 template<typename T> static void norm(T& x, T& y) {
216#if defined(_MSC_VER) && defined(_M_IX86)
217 // hypot for Visual Studio (A=win32) fails monotonicity, e.g., with
218 // x = 0.6102683302836215
219 // y1 = 0.7906090004346522
220 // y2 = y1 + 1e-16
221 // the test
222 // hypot(x, y2) >= hypot(x, y1)
223 // fails. Reported 2021-03-14:
224 // https://developercommunity.visualstudio.com/t/1369259
225 // See also:
226 // https://bugs.python.org/issue43088
227 using std::sqrt; T h = sqrt(x * x + y * y);
228#else
229 using std::hypot; T h = hypot(x, y);
230#endif
231 x /= h; y /= h;
232 }
233
234 /**
235 * The error-free sum of two numbers.
236 *
237 * @tparam T the type of the argument and the returned value.
238 * @param[in] u
239 * @param[in] v
240 * @param[out] t the exact error given by (\e u + \e v) - \e s.
241 * @return \e s = round(\e u + \e v).
242 *
243 * See D. E. Knuth, TAOCP, Vol 2, 4.2.2, Theorem B.
244 *
245 * \note \e t can be the same as one of the first two arguments.
246 **********************************************************************/
247 template<typename T> static T sum(T u, T v, T& t);
248
249 /**
250 * Evaluate a polynomial.
251 *
252 * @tparam T the type of the arguments and returned value.
253 * @param[in] N the order of the polynomial.
254 * @param[in] p the coefficient array (of size \e N + 1) with
255 * <i>p</i><sub>0</sub> being coefficient of <i>x</i><sup><i>N</i></sup>.
256 * @param[in] x the variable.
257 * @return the value of the polynomial.
258 *
259 * Evaluate &sum;<sub><i>n</i>=0..<i>N</i></sub>
260 * <i>p</i><sub><i>n</i></sub> <i>x</i><sup><i>N</i>&minus;<i>n</i></sup>.
261 * Return 0 if \e N &lt; 0. Return <i>p</i><sub>0</sub>, if \e N = 0 (even
262 * if \e x is infinite or a nan). The evaluation uses Horner's method.
263 **********************************************************************/
264 template<typename T> static T polyval(int N, const T p[], T x) {
265 // This used to employ Math::fma; but that's too slow and it seemed not
266 // to improve the accuracy noticeably. This might change when there's
267 // direct hardware support for fma.
268 T y = N < 0 ? 0 : *p++;
269 while (--N >= 0) y = y * x + *p++;
270 return y;
271 }
272
273 /**
274 * Normalize an angle.
275 *
276 * @tparam T the type of the argument and returned value.
277 * @param[in] x the angle in degrees.
278 * @return the angle reduced to the range [&minus;180&deg;, 180&deg;].
279 *
280 * The range of \e x is unrestricted. If the result is &plusmn;0&deg; or
281 * &plusmn;180&deg; then the sign is the sign of \e x.
282 **********************************************************************/
283 template<typename T> static T AngNormalize(T x);
284
285 /**
286 * Normalize a latitude.
287 *
288 * @tparam T the type of the argument and returned value.
289 * @param[in] x the angle in degrees.
290 * @return x if it is in the range [&minus;90&deg;, 90&deg;], otherwise
291 * return NaN.
292 **********************************************************************/
293 template<typename T> static T LatFix(T x)
294 { using std::fabs; return fabs(x) > T(qd) ? NaN<T>() : x; }
295
296 /**
297 * The exact difference of two angles reduced to
298 * [&minus;180&deg;, 180&deg;].
299 *
300 * @tparam T the type of the arguments and returned value.
301 * @param[in] x the first angle in degrees.
302 * @param[in] y the second angle in degrees.
303 * @param[out] e the error term in degrees.
304 * @return \e d, the truncated value of \e y &minus; \e x.
305 *
306 * This computes \e z = \e y &minus; \e x exactly, reduced to
307 * [&minus;180&deg;, 180&deg;]; and then sets \e z = \e d + \e e where \e d
308 * is the nearest representable number to \e z and \e e is the truncation
309 * error. If \e z = &plusmn;0&deg; or &plusmn;180&deg;, then the sign of
310 * \e d is given by the sign of \e y &minus; \e x. The maximum absolute
311 * value of \e e is 2<sup>&minus;26</sup> (for doubles).
312 **********************************************************************/
313 template<typename T> static T AngDiff(T x, T y, T& e);
314
315 /**
316 * Difference of two angles reduced to [&minus;180&deg;, 180&deg;]
317 *
318 * @tparam T the type of the arguments and returned value.
319 * @param[in] x the first angle in degrees.
320 * @param[in] y the second angle in degrees.
321 * @return \e y &minus; \e x, reduced to the range [&minus;180&deg;,
322 * 180&deg;].
323 *
324 * The result is equivalent to computing the difference exactly, reducing
325 * it to [&minus;180&deg;, 180&deg;] and rounding the result.
326 **********************************************************************/
327 template<typename T> static T AngDiff(T x, T y)
328 { T e; return AngDiff(x, y, e); }
329
330 /**
331 * Coarsen a value close to zero.
332 *
333 * @tparam T the type of the argument and returned value.
334 * @param[in] x
335 * @return the coarsened value.
336 *
337 * The makes the smallest gap in \e x = 1/16 &minus; nextafter(1/16, 0) =
338 * 1/2<sup>57</sup> for doubles = 0.8 pm on the earth if \e x is an angle
339 * in degrees. (This is about 2000 times more resolution than we get with
340 * angles around 90&deg;.) We use this to avoid having to deal with near
341 * singular cases when \e x is non-zero but tiny (e.g.,
342 * 10<sup>&minus;200</sup>). This sign of &plusmn;0 is preserved.
343 **********************************************************************/
344 template<typename T> static T AngRound(T x);
345
346 /**
347 * Evaluate the sine and cosine function with the argument in degrees
348 *
349 * @tparam T the type of the arguments.
350 * @param[in] x in degrees.
351 * @param[out] sinx sin(<i>x</i>).
352 * @param[out] cosx cos(<i>x</i>).
353 *
354 * The results obey exactly the elementary properties of the trigonometric
355 * functions, e.g., sin 9&deg; = cos 81&deg; = &minus; sin 123456789&deg;.
356 * If x = &minus;0 or a negative multiple of 180&deg;, then \e sinx =
357 * &minus;0; this is the only case where &minus;0 is returned.
358 **********************************************************************/
359 template<typename T> static void sincosd(T x, T& sinx, T& cosx);
360
361 /**
362 * Evaluate the sine and cosine with reduced argument plus correction
363 *
364 * @tparam T the type of the arguments.
365 * @param[in] x reduced angle in degrees.
366 * @param[in] t correction in degrees.
367 * @param[out] sinx sin(<i>x</i> + <i>t</i>).
368 * @param[out] cosx cos(<i>x</i> + <i>t</i>).
369 *
370 * This is a variant of Math::sincosd allowing a correction to the angle to
371 * be supplied. \e x must be in [&minus;180&deg;, 180&deg;] and \e t is
372 * assumed to be a <i>small</i> correction. Math::AngRound is applied to
373 * the reduced angle to prevent problems with \e x + \e t being extremely
374 * close but not exactly equal to one of the four cardinal directions.
375 **********************************************************************/
376 template<typename T> static void sincosde(T x, T t, T& sinx, T& cosx);
377
378 /**
379 * Evaluate the sine function with the argument in degrees
380 *
381 * @tparam T the type of the argument and the returned value.
382 * @param[in] x in degrees.
383 * @return sin(<i>x</i>).
384 *
385 * The result is +0 for \e x = +0 and positive multiples of 180&deg;. The
386 * result is &minus;0 for \e x = -0 and negative multiples of 180&deg;.
387 **********************************************************************/
388 template<typename T> static T sind(T x);
389
390 /**
391 * Evaluate the cosine function with the argument in degrees
392 *
393 * @tparam T the type of the argument and the returned value.
394 * @param[in] x in degrees.
395 * @return cos(<i>x</i>).
396 *
397 * The result is +0 for \e x an odd multiple of 90&deg;.
398 **********************************************************************/
399 template<typename T> static T cosd(T x);
400
401 /**
402 * Evaluate the tangent function with the argument in degrees
403 *
404 * @tparam T the type of the argument and the returned value.
405 * @param[in] x in degrees.
406 * @return tan(<i>x</i>).
407 *
408 * If \e x is an odd multiple of 90&deg;, then a suitably large (but
409 * finite) value is returned.
410 **********************************************************************/
411 template<typename T> static T tand(T x);
412
413 /**
414 * Evaluate the atan2 function with the result in degrees
415 *
416 * @tparam T the type of the arguments and the returned value.
417 * @param[in] y
418 * @param[in] x
419 * @return atan2(<i>y</i>, <i>x</i>) in degrees.
420 *
421 * The result is in the range [&minus;180&deg; 180&deg;]. N.B.,
422 * atan2d(&plusmn;0, &minus;1) = &plusmn;180&deg;.
423 **********************************************************************/
424 template<typename T> static T atan2d(T y, T x);
425
426 /**
427 * Evaluate the atan function with the result in degrees
428 *
429 * @tparam T the type of the argument and the returned value.
430 * @param[in] x
431 * @return atan(<i>x</i>) in degrees.
432 **********************************************************************/
433 template<typename T> static T atand(T x);
434
435 /**
436 * Evaluate <i>e</i> atanh(<i>e x</i>)
437 *
438 * @tparam T the type of the argument and the returned value.
439 * @param[in] x
440 * @param[in] es the signed eccentricity = sign(<i>e</i><sup>2</sup>)
441 * sqrt(|<i>e</i><sup>2</sup>|)
442 * @return <i>e</i> atanh(<i>e x</i>)
443 *
444 * If <i>e</i><sup>2</sup> is negative (<i>e</i> is imaginary), the
445 * expression is evaluated in terms of atan.
446 **********************************************************************/
447 template<typename T> static T eatanhe(T x, T es);
448
449 /**
450 * tan&chi; in terms of tan&phi;
451 *
452 * @tparam T the type of the argument and the returned value.
453 * @param[in] tau &tau; = tan&phi;
454 * @param[in] es the signed eccentricity = sign(<i>e</i><sup>2</sup>)
455 * sqrt(|<i>e</i><sup>2</sup>|)
456 * @return &tau;&prime; = tan&chi;
457 *
458 * See Eqs. (7--9) of
459 * C. F. F. Karney,
460 * <a href="https://doi.org/10.1007/s00190-011-0445-3">
461 * Transverse Mercator with an accuracy of a few nanometers,</a>
462 * J. Geodesy 85(8), 475--485 (Aug. 2011)
463 * (preprint
464 * <a href="https://arxiv.org/abs/1002.1417">arXiv:1002.1417</a>).
465 **********************************************************************/
466 template<typename T> static T taupf(T tau, T es);
467
468 /**
469 * tan&phi; in terms of tan&chi;
470 *
471 * @tparam T the type of the argument and the returned value.
472 * @param[in] taup &tau;&prime; = tan&chi;
473 * @param[in] es the signed eccentricity = sign(<i>e</i><sup>2</sup>)
474 * sqrt(|<i>e</i><sup>2</sup>|)
475 * @return &tau; = tan&phi;
476 *
477 * See Eqs. (19--21) of
478 * C. F. F. Karney,
479 * <a href="https://doi.org/10.1007/s00190-011-0445-3">
480 * Transverse Mercator with an accuracy of a few nanometers,</a>
481 * J. Geodesy 85(8), 475--485 (Aug. 2011)
482 * (preprint
483 * <a href="https://arxiv.org/abs/1002.1417">arXiv:1002.1417</a>).
484 **********************************************************************/
485 template<typename T> static T tauf(T taup, T es);
486
487 /**
488 * The NaN (not a number)
489 *
490 * @tparam T the type of the returned value.
491 * @return NaN if available, otherwise return the max real of type T.
492 **********************************************************************/
493 template<typename T = real> static T NaN();
494
495 /**
496 * Infinity
497 *
498 * @tparam T the type of the returned value.
499 * @return infinity if available, otherwise return the max real.
500 **********************************************************************/
501 template<typename T = real> static T infinity();
502
503 /**
504 * Swap the bytes of a quantity
505 *
506 * @tparam T the type of the argument and the returned value.
507 * @param[in] x
508 * @return x with its bytes swapped.
509 **********************************************************************/
510 template<typename T> static T swab(T x) {
511 union {
512 T r;
513 unsigned char c[sizeof(T)];
514 } b;
515 b.r = x;
516 for (int i = sizeof(T)/2; i--; )
517 std::swap(b.c[i], b.c[sizeof(T) - 1 - i]);
518 return b.r;
519 }
520
521 };
522
523} // namespace GeographicLib
524
525#endif // GEOGRAPHICLIB_MATH_HPP
Constants.hpp
Header for GeographicLib::Constants class.
GEOGRAPHICLIB_EXPORT
#define GEOGRAPHICLIB_EXPORT
Definition: Constants.hpp:67
real
GeographicLib::Math::real real
Definition: GeodSolve.cpp:29
GEOGRAPHICLIB_WORDS_BIGENDIAN
#define GEOGRAPHICLIB_WORDS_BIGENDIAN
Definition: Math.hpp:18
GeographicLib::Math
Mathematical functions needed by GeographicLib.
Definition: Math.hpp:77
GeographicLib::Math::degree
static T degree()
Definition: Math.hpp:193
GeographicLib::Math::LatFix
static T LatFix(T x)
Definition: Math.hpp:293
GeographicLib::Math::extended
double extended
Definition: Math.hpp:90
GeographicLib::Math::norm
static void norm(T &x, T &y)
Definition: Math.hpp:215
GeographicLib::Math::sq
static T sq(T x)
Definition: Math.hpp:205
GeographicLib::Math::pi
static T pi()
Definition: Math.hpp:183
GeographicLib::Math::polyval
static T polyval(int N, const T p[], T x)
Definition: Math.hpp:264
GeographicLib::Math::AngDiff
static T AngDiff(T x, T y)
Definition: Math.hpp:327
GeographicLib::Math::swab
static T swab(T x)
Definition: Math.hpp:510
GeographicLib::Math::real
double real
Definition: Math.hpp:100
GeographicLib::Math::dms
dms
Definition: Math.hpp:137
GeographicLib
Namespace for GeographicLib.
Definition: Accumulator.cpp:12
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