|
- /**
- * \file Math.hpp
- * \brief Header for GeographicLib::Math class
- *
- * Copyright (c) Charles Karney (2008-2017) <charles@karney.com> and licensed
- * under the MIT/X11 License. For more information, see
- * https://geographiclib.sourceforge.io/
- **********************************************************************/
-
- #pragma once
-
- #include <algorithm>
- #include <cmath>
- #include <limits>
-
- #if defined(OS_WINDOWS) || defined(OS_ANDROID) || defined(OS_QNX)
- #define GEOGRAPHICLIB_CXX11_MATH 0
- #else
- #define GEOGRAPHICLIB_CXX11_MATH 1
- #endif
-
- #if GEOGRAPHICLIB_CXX11_MATH == 1
- #define GEOGRAPHICLIB_STATIC_ASSERT static_assert
- #else
- #define GEOGRAPHICLIB_STATIC_ASSERT static_assertC
- #endif
-
- #define GEOGRAPHICLIB_EXPORT
- #define GEOGRAPHICLIB_VOLATILE volatile
- #define GEOGRAPHICLIB_PANIC false
- #define GEOGRAPHICLIB_PRECISION 2
-
- namespace GeographicLib {
-
- class Math
- {
- public:
- typedef double real;
-
- /**
- * @return the number of bits of precision in a real number.
- **********************************************************************/
- static inline int digits() { return std::numeric_limits<real>::digits; }
-
- /**
- * @tparam T the type of the returned value.
- * @return π.
- **********************************************************************/
- template <typename T>
- static inline T pi()
- {
- using std::atan2;
- static const T pi = atan2(T(0), T(-1));
- return pi;
- }
- /**
- * A synonym for pi<real>().
- **********************************************************************/
- static inline real pi() { return pi<real>(); }
-
- /**
- * @tparam T the type of the returned value.
- * @return the number of radians in a degree.
- **********************************************************************/
- template <typename T>
- static inline T degree()
- {
- static const T degree = pi<T>() / 180;
- return degree;
- }
- /**
- * A synonym for degree<real>().
- **********************************************************************/
- static inline real degree() { return degree<real>(); }
-
- /**
- * Square a number.
- *
- * @tparam T the type of the argument and the returned value.
- * @param[in] x
- * @return <i>x</i><sup>2</sup>.
- **********************************************************************/
- template <typename T>
- static inline T sq(T x)
- {
- return x * x;
- }
-
- /**
- * The hypotenuse function avoiding underflow and overflow.
- *
- * @tparam T the type of the arguments and the returned value.
- * @param[in] x
- * @param[in] y
- * @return sqrt(<i>x</i><sup>2</sup> + <i>y</i><sup>2</sup>).
- **********************************************************************/
- template <typename T>
- static inline T hypot(T x, T y)
- {
- #if GEOGRAPHICLIB_CXX11_MATH
- using std::hypot;
- return hypot(x, y);
- #else
- using std::abs;
- using std::sqrt;
- x = abs(x);
- y = abs(y);
-
- if (x < y)
- {
- std::swap(x, y); // Now x >= y >= 0
- }
-
- y /= (x ? x : 1);
- return x * sqrt(1 + y * y);
- // For an alternative (square-root free) method see
- // C. Moler and D. Morrision (1983) https://doi.org/10.1147/rd.276.0577
- // and A. A. Dubrulle (1983) https://doi.org/10.1147/rd.276.0582
- #endif
- }
-
- /**
- * The cube root function.
- *
- * @tparam T the type of the argument and the returned value.
- * @param[in] x
- * @return the real cube root of \e x.
- **********************************************************************/
- template <typename T>
- static inline T cbrt(T x)
- {
- #if GEOGRAPHICLIB_CXX11_MATH
- using std::cbrt;
- return cbrt(x);
- #else
- using std::abs;
- using std::pow;
- T y = pow(abs(x), 1 / T(3)); // Return the real cube root
- return x < 0 ? -y : y;
- #endif
- }
-
- /**
- * Normalize a two-vector.
- *
- * @tparam T the type of the argument and the returned value.
- * @param[in,out] x on output set to <i>x</i>/hypot(<i>x</i>, <i>y</i>).
- * @param[in,out] y on output set to <i>y</i>/hypot(<i>x</i>, <i>y</i>).
- **********************************************************************/
- template <typename T>
- static inline void norm(T &x, T &y)
- {
- T h = hypot(x, y);
- x /= h;
- y /= h;
- }
-
- /**
- * The error-free sum of two numbers.
- *
- * @tparam T the type of the argument and the returned value.
- * @param[in] u
- * @param[in] v
- * @param[out] t the exact error given by (\e u + \e v) - \e s.
- * @return \e s = round(\e u + \e v).
- *
- * See D. E. Knuth, TAOCP, Vol 2, 4.2.2, Theorem B. (Note that \e t can be
- * the same as one of the first two arguments.)
- **********************************************************************/
- template <typename T>
- static inline T sum(T u, T v, T &t)
- {
- GEOGRAPHICLIB_VOLATILE T s = u + v;
- GEOGRAPHICLIB_VOLATILE T up = s - v;
- GEOGRAPHICLIB_VOLATILE T vpp = s - up;
- up -= u;
- vpp -= v;
- t = -(up + vpp);
- // u + v = s + t
- // = round(u + v) + t
- return s;
- }
-
- /**
- * Evaluate a polynomial.
- *
- * @tparam T the type of the arguments and returned value.
- * @param[in] N the order of the polynomial.
- * @param[in] p the coefficient array (of size \e N + 1).
- * @param[in] x the variable.
- * @return the value of the polynomial.
- *
- * Evaluate <i>y</i> = ∑<sub><i>n</i>=0..<i>N</i></sub>
- * <i>p</i><sub><i>n</i></sub> <i>x</i><sup><i>N</i>−<i>n</i></sup>.
- * Return 0 if \e N < 0. Return <i>p</i><sub>0</sub>, if \e N = 0 (even
- * if \e x is infinite or a nan). The evaluation uses Horner's method.
- **********************************************************************/
- template <typename T>
- static inline T polyval(int N, const T p[], T x)
- {
- T y = N < 0 ? 0 : *p++;
-
- while (--N >= 0)
- {
- y = y * x + *p++;
- }
-
- return y;
- }
-
- /**
- * Normalize an angle.
- *
- * @tparam T the type of the argument and returned value.
- * @param[in] x the angle in degrees.
- * @return the angle reduced to the range([−180°, 180°].
- *
- * The range of \e x is unrestricted.
- **********************************************************************/
- template <typename T>
- static inline T AngNormalize(T x)
- {
- #if GEOGRAPHICLIB_CXX11_MATH && GEOGRAPHICLIB_PRECISION != 4
- using std::remainder;
- x = remainder(x, T(360));
- return x != -180 ? x : 180;
- #else
- using std::fmod;
- T y = fmod(x, T(360));
- #if defined(_MSC_VER) && _MSC_VER < 1900
-
- // Before version 14 (2015), Visual Studio had problems dealing
- // with -0.0. Specifically
- // VC 10,11,12 and 32-bit compile: fmod(-0.0, 360.0) -> +0.0
- // sincosd has a similar fix.
- // python 2.7 on Windows 32-bit machines has the same problem.
- if (x == 0)
- {
- y = x;
- }
-
- #endif
- return y <= -180 ? y + 360 : (y <= 180 ? y : y - 360);
- #endif
- }
-
- /**
- * Normalize a latitude.
- *
- * @tparam T the type of the argument and returned value.
- * @param[in] x the angle in degrees.
- * @return x if it is in the range [−90°, 90°], otherwise
- * return NaN.
- **********************************************************************/
- template <typename T>
- static inline T LatFix(T x)
- {
- using std::abs;
- return abs(x) > 90 ? NaN<T>() : x;
- }
-
- /**
- * The exact difference of two angles reduced to
- * (−180°, 180°].
- *
- * @tparam T the type of the arguments and returned value.
- * @param[in] x the first angle in degrees.
- * @param[in] y the second angle in degrees.
- * @param[out] e the error term in degrees.
- * @return \e d, the truncated value of \e y − \e x.
- *
- * This computes \e z = \e y − \e x exactly, reduced to
- * (−180°, 180°]; and then sets \e z = \e d + \e e where \e d
- * is the nearest representable number to \e z and \e e is the truncation
- * error. If \e d = −180, then \e e > 0; If \e d = 180, then \e e
- * ≤ 0.
- **********************************************************************/
- template <typename T>
- static inline T AngDiff(T x, T y, T &e)
- {
- #if GEOGRAPHICLIB_CXX11_MATH && GEOGRAPHICLIB_PRECISION != 4
- using std::remainder;
- T t, d = AngNormalize(
- sum(remainder(-x, T(360)), remainder(y, T(360)), t));
- #else
- T t, d = AngNormalize(sum(AngNormalize(-x), AngNormalize(y), t));
- #endif
- // Here y - x = d + t (mod 360), exactly, where d is in (-180,180] and
- // abs(t) <= eps (eps = 2^-45 for doubles). The only case where the
- // addition of t takes the result outside the range (-180,180] is d =
- // 180 and t > 0. The case, d = -180 + eps, t = -eps, can't happen,
- // since sum would have returned the exact result in such a case (i.e.,
- // given t = 0).
- return sum(d == 180 && t > 0 ? -180 : d, t, e);
- }
-
- /**
- * Coarsen a value close to zero.
- *
- * @tparam T the type of the argument and returned value.
- * @param[in] x
- * @return the coarsened value.
- *
- * The makes the smallest gap in \e x = 1/16 - nextafter(1/16, 0) =
- * 1/2<sup>57</sup> for reals = 0.7 pm on the earth if \e x is an angle in
- * degrees. (This is about 1000 times more resolution than we get with
- * angles around 90°.) We use this to avoid having to deal with near
- * singular cases when \e x is non-zero but tiny (e.g.,
- * 10<sup>−200</sup>). This converts -0 to +0; however tiny negative
- * numbers get converted to -0.
- **********************************************************************/
- template <typename T>
- static inline T AngRound(T x)
- {
- using std::abs;
- static const T z = 1 / T(16);
-
- if (x == 0)
- {
- return 0;
- }
-
- GEOGRAPHICLIB_VOLATILE T y = abs(x);
- // The compiler mustn't "simplify" z - (z - y) to y
- y = y < z ? z - (z - y) : y;
- return x < 0 ? -y : y;
- }
-
- /**
- * Evaluate the sine and cosine function with the argument in degrees
- *
- * @tparam T the type of the arguments.
- * @param[in] x in degrees.
- * @param[out] sinx sin(<i>x</i>).
- * @param[out] cosx cos(<i>x</i>).
- *
- * The results obey exactly the elementary properties of the trigonometric
- * functions, e.g., sin 9° = cos 81° = − sin 123456789°.
- * If x = −0, then \e sinx = −0; this is the only case where
- * −0 is returned.
- **********************************************************************/
- template <typename T>
- static inline void sincosd(T x, T &sinx, T &cosx)
- {
- // In order to minimize round-off errors, this function exactly reduces
- // the argument to the range [-45, 45] before converting it to radians.
- using std::cos;
- using std::sin;
- T r;
- int q;
- #if GEOGRAPHICLIB_CXX11_MATH && GEOGRAPHICLIB_PRECISION <= 3 && \
- !defined(__GNUC__)
- // Disable for gcc because of bug in glibc version < 2.22, see
- // https://sourceware.org/bugzilla/show_bug.cgi?id=17569
- // Once this fix is widely deployed, should insert a runtime test for
- // the glibc version number. For example
- // #include <gnu/libc-version.h>
- // std::string version(gnu_get_libc_version()); => "2.22"
- using std::remquo;
- r = remquo(x, T(90), &q);
- #else
- using std::floor;
- using std::fmod;
- r = fmod(x, T(360));
- q = int(floor(r / 90 + T(0.5)));
- r -= 90 * q;
- #endif
- // now abs(r) <= 45
- r *= degree();
- // Possibly could call the gnu extension sincos
- T s = sin(r), c = cos(r);
- #if defined(_MSC_VER) && _MSC_VER < 1900
-
- // Before version 14 (2015), Visual Studio had problems dealing
- // with -0.0. Specifically
- // VC 10,11,12 and 32-bit compile: fmod(-0.0, 360.0) -> +0.0
- // VC 12 and 64-bit compile: sin(-0.0) -> +0.0
- // AngNormalize has a similar fix.
- // python 2.7 on Windows 32-bit machines has the same problem.
- if (x == 0)
- {
- s = x;
- }
-
- #endif
-
- switch (unsigned(q) & 3U)
- {
- case 0U:
- sinx = s;
- cosx = c;
- break;
-
- case 1U:
- sinx = c;
- cosx = -s;
- break;
-
- case 2U:
- sinx = -s;
- cosx = -c;
- break;
-
- default:
- sinx = -c;
- cosx = s;
- break; // case 3U
- }
-
- // Set sign of 0 results. -0 only produced for sin(-0)
- if (x)
- {
- sinx += T(0);
- cosx += T(0);
- }
- }
-
- /**
- * Evaluate <i>e</i> atanh(<i>e x</i>)
- *
- * @tparam T the type of the argument and the returned value.
- * @param[in] x
- * @param[in] es the signed eccentricity = sign(<i>e</i><sup>2</sup>)
- * sqrt(|<i>e</i><sup>2</sup>|)
- * @return <i>e</i> atanh(<i>e x</i>)
- *
- * If <i>e</i><sup>2</sup> is negative (<i>e</i> is imaginary), the
- * expression is evaluated in terms of atan.
- **********************************************************************/
-
- template <typename T>
- static T atanh(T x)
- {
- return ((double)1 / 2) * (log((1 + x) / (1 - x)));
- }
-
- template <typename T>
- static T eatanhe(T x, T es)
- {
- return es > T(0) ? es * atanh(es * x) : -es * atan(es * x);
- }
-
- /**
- * Test for finiteness.
- *
- * @tparam T the type of the argument.
- * @param[in] x
- * @return true if number is finite, false if NaN or infinite.
- **********************************************************************/
- template <typename T>
- static inline bool isfinite(T x)
- {
- #if GEOGRAPHICLIB_CXX11_MATH
- using std::isfinite;
- return isfinite(x);
- #else
- using std::abs;
- return abs(x) <= std::numeric_limits<T>::max();
- #endif
- }
-
- /**
- * The NaN (not a number)
- *
- * @tparam T the type of the returned value.
- * @return NaN if available, otherwise return the max real of type T.
- **********************************************************************/
- template <typename T>
- static inline T NaN()
- {
- return std::numeric_limits<T>::has_quiet_NaN
- ? std::numeric_limits<T>::quiet_NaN()
- : std::numeric_limits<T>::max();
- }
- };
-
- } // namespace GeographicLib
|