123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684 |
- /**
- * \file Math.hpp
- * \brief Header for GeographicLib::Math class
- *
- * Copyright (c) Charles Karney (2008-2020) <charles@karney.com> and licensed
- * under the MIT/X11 License. For more information, see
- * https://geographiclib.sourceforge.io/
- **********************************************************************/
- // Constants.hpp includes Math.hpp. Place this include outside Math.hpp's
- // include guard to enforce this ordering.
- #include <GeographicLib/Constants.hpp>
- #if !defined(GEOGRAPHICLIB_MATH_HPP)
- #define GEOGRAPHICLIB_MATH_HPP 1
- #if !defined(GEOGRAPHICLIB_WORDS_BIGENDIAN)
- # define GEOGRAPHICLIB_WORDS_BIGENDIAN 0
- #endif
- #if !defined(GEOGRAPHICLIB_HAVE_LONG_DOUBLE)
- # define GEOGRAPHICLIB_HAVE_LONG_DOUBLE 0
- #endif
- #if !defined(GEOGRAPHICLIB_PRECISION)
- /**
- * The precision of floating point numbers used in %GeographicLib. 1 means
- * float (single precision); 2 (the default) means double; 3 means long double;
- * 4 is reserved for quadruple precision. Nearly all the testing has been
- * carried out with doubles and that's the recommended configuration. In order
- * for long double to be used, GEOGRAPHICLIB_HAVE_LONG_DOUBLE needs to be
- * defined. Note that with Microsoft Visual Studio, long double is the same as
- * double.
- **********************************************************************/
- # define GEOGRAPHICLIB_PRECISION 2
- #endif
- #include <cmath>
- #include <algorithm>
- #include <limits>
- #if GEOGRAPHICLIB_PRECISION == 4
- #include <boost/version.hpp>
- #include <boost/multiprecision/float128.hpp>
- #include <boost/math/special_functions.hpp>
- #elif GEOGRAPHICLIB_PRECISION == 5
- #include <mpreal.h>
- #endif
- #if GEOGRAPHICLIB_PRECISION > 3
- // volatile keyword makes no sense for multiprec types
- #define GEOGRAPHICLIB_VOLATILE
- // Signal a convergence failure with multiprec types by throwing an exception
- // at loop exit.
- #define GEOGRAPHICLIB_PANIC \
- (throw GeographicLib::GeographicErr("Convergence failure"), false)
- #else
- #define GEOGRAPHICLIB_VOLATILE volatile
- // Ignore convergence failures with standard floating points types by allowing
- // loop to exit cleanly.
- #define GEOGRAPHICLIB_PANIC false
- #endif
- namespace GeographicLib {
- /**
- * \brief Mathematical functions needed by %GeographicLib
- *
- * Define mathematical functions in order to localize system dependencies and
- * to provide generic versions of the functions. In addition define a real
- * type to be used by %GeographicLib.
- *
- * Example of use:
- * \include example-Math.cpp
- **********************************************************************/
- class GEOGRAPHICLIB_EXPORT Math {
- private:
- void dummy(); // Static check for GEOGRAPHICLIB_PRECISION
- Math(); // Disable constructor
- public:
- #if GEOGRAPHICLIB_HAVE_LONG_DOUBLE
- /**
- * The extended precision type for real numbers, used for some testing.
- * This is long double on computers with this type; otherwise it is double.
- **********************************************************************/
- typedef long double extended;
- #else
- typedef double extended;
- #endif
- #if GEOGRAPHICLIB_PRECISION == 2
- /**
- * The real type for %GeographicLib. Nearly all the testing has been done
- * with \e real = double. However, the algorithms should also work with
- * float and long double (where available). (<b>CAUTION</b>: reasonable
- * accuracy typically cannot be obtained using floats.)
- **********************************************************************/
- typedef double real;
- #elif GEOGRAPHICLIB_PRECISION == 1
- typedef float real;
- #elif GEOGRAPHICLIB_PRECISION == 3
- typedef extended real;
- #elif GEOGRAPHICLIB_PRECISION == 4
- typedef boost::multiprecision::float128 real;
- #elif GEOGRAPHICLIB_PRECISION == 5
- typedef mpfr::mpreal real;
- #else
- typedef double real;
- #endif
- /**
- * @return the number of bits of precision in a real number.
- **********************************************************************/
- static int digits();
- /**
- * Set the binary precision of a real number.
- *
- * @param[in] ndigits the number of bits of precision.
- * @return the resulting number of bits of precision.
- *
- * This only has an effect when GEOGRAPHICLIB_PRECISION = 5. See also
- * Utility::set_digits for caveats about when this routine should be
- * called.
- **********************************************************************/
- static int set_digits(int ndigits);
- /**
- * @return the number of decimal digits of precision in a real number.
- **********************************************************************/
- static int digits10();
- /**
- * Number of additional decimal digits of precision for real relative to
- * double (0 for float).
- **********************************************************************/
- static int extra_digits();
- /**
- * true if the machine is big-endian.
- **********************************************************************/
- static const bool bigendian = GEOGRAPHICLIB_WORDS_BIGENDIAN;
- /**
- * @tparam T the type of the returned value.
- * @return π.
- **********************************************************************/
- template<typename T = real> static T pi() {
- using std::atan2;
- static const T pi = atan2(T(0), T(-1));
- return pi;
- }
- /**
- * @tparam T the type of the returned value.
- * @return the number of radians in a degree.
- **********************************************************************/
- template<typename T = real> static T degree() {
- static const T degree = pi<T>() / 180;
- return degree;
- }
- /**
- * 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 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>).
- *
- * \deprecated Use std::hypot(x, y).
- **********************************************************************/
- template<typename T>
- GEOGRAPHICLIB_DEPRECATED("Use std::hypot(x, y)")
- static T hypot(T x, T y);
- /**
- * exp(\e x) − 1 accurate near \e x = 0.
- *
- * @tparam T the type of the argument and the returned value.
- * @param[in] x
- * @return exp(\e x) − 1.
- *
- * \deprecated Use std::expm1(x).
- **********************************************************************/
- template<typename T>
- GEOGRAPHICLIB_DEPRECATED("Use std::expm1(x)")
- static T expm1(T x);
- /**
- * log(1 + \e x) accurate near \e x = 0.
- *
- * @tparam T the type of the argument and the returned value.
- * @param[in] x
- * @return log(1 + \e x).
- *
- * \deprecated Use std::log1p(x).
- **********************************************************************/
- template<typename T>
- GEOGRAPHICLIB_DEPRECATED("Use std::log1p(x)")
- static T log1p(T x);
- /**
- * The inverse hyperbolic sine function.
- *
- * @tparam T the type of the argument and the returned value.
- * @param[in] x
- * @return asinh(\e x).
- *
- * \deprecated Use std::asinh(x).
- **********************************************************************/
- template<typename T>
- GEOGRAPHICLIB_DEPRECATED("Use std::asinh(x)")
- static T asinh(T x);
- /**
- * The inverse hyperbolic tangent function.
- *
- * @tparam T the type of the argument and the returned value.
- * @param[in] x
- * @return atanh(\e x).
- *
- * \deprecated Use std::atanh(x).
- **********************************************************************/
- template<typename T>
- GEOGRAPHICLIB_DEPRECATED("Use std::atanh(x)")
- static T atanh(T x);
- /**
- * Copy the sign.
- *
- * @tparam T the type of the argument.
- * @param[in] x gives the magitude of the result.
- * @param[in] y gives the sign of the result.
- * @return value with the magnitude of \e x and with the sign of \e y.
- *
- * This routine correctly handles the case \e y = −0, returning
- * &minus|<i>x</i>|.
- *
- * \deprecated Use std::copysign(x, y).
- **********************************************************************/
- template<typename T>
- GEOGRAPHICLIB_DEPRECATED("Use std::copysign(x, y)")
- static T copysign(T x, T y);
- /**
- * 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.
- *
- * \deprecated Use std::cbrt(x).
- **********************************************************************/
- template<typename T>
- GEOGRAPHICLIB_DEPRECATED("Use std::cbrt(x)")
- static T cbrt(T x);
- /**
- * The remainder function.
- *
- * @tparam T the type of the arguments and the returned value.
- * @param[in] x
- * @param[in] y
- * @return the remainder of \e x/\e y in the range [−\e y/2, \e y/2].
- *
- * \deprecated Use std::remainder(x).
- **********************************************************************/
- template<typename T>
- GEOGRAPHICLIB_DEPRECATED("Use std::remainder(x)")
- static T remainder(T x, T y);
- /**
- * The remquo function.
- *
- * @tparam T the type of the arguments and the returned value.
- * @param[in] x
- * @param[in] y
- * @param[out] n the low 3 bits of the quotient
- * @return the remainder of \e x/\e y in the range [−\e y/2, \e y/2].
- *
- * \deprecated Use std::remquo(x, y, n).
- **********************************************************************/
- template<typename T>
- GEOGRAPHICLIB_DEPRECATED("Use std::remquo(x, y, n)")
- static T remquo(T x, T y, int* n);
- /**
- * The round function.
- *
- * @tparam T the type of the argument and the returned value.
- * @param[in] x
- * @return \e x round to the nearest integer (ties round away from 0).
- *
- * \deprecated Use std::round(x).
- **********************************************************************/
- template<typename T>
- GEOGRAPHICLIB_DEPRECATED("Use std::round(x)")
- static T round(T x);
- /**
- * The lround function.
- *
- * @tparam T the type of the argument.
- * @param[in] x
- * @return \e x round to the nearest integer as a long int (ties round away
- * from 0).
- *
- * If the result does not fit in a long int, the return value is undefined.
- *
- * \deprecated Use std::lround(x).
- **********************************************************************/
- template<typename T>
- GEOGRAPHICLIB_DEPRECATED("Use std::lround(x)")
- static long lround(T x);
- /**
- * Fused multiply and add.
- *
- * @tparam T the type of the arguments and the returned value.
- * @param[in] x
- * @param[in] y
- * @param[in] z
- * @return <i>xy</i> + <i>z</i>, correctly rounded (on those platforms with
- * support for the <code>fma</code> instruction).
- *
- * On platforms without the <code>fma</code> instruction, no attempt is
- * made to improve on the result of a rounded multiplication followed by a
- * rounded addition.
- *
- * \deprecated Use std::fma(x, y, z).
- **********************************************************************/
- template<typename T>
- GEOGRAPHICLIB_DEPRECATED("Use std::fma(x, y, z)")
- static T fma(T x, T y, T z);
- /**
- * 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 void norm(T& x, T& y) {
- #if defined(_MSC_VER) && defined(_M_IX86)
- // hypot for Visual Studio (A=win32) fails monotonicity, e.g., with
- // x = 0.6102683302836215
- // y1 = 0.7906090004346522
- // y2 = y1 + 1e-16
- // the test
- // hypot(x, y2) >= hypot(x, y1)
- // fails. Reported 2021-03-14:
- // https://developercommunity.visualstudio.com/t/1369259
- // See also:
- // https://bugs.python.org/issue43088
- using std::sqrt; T h = sqrt(x * x + y * y);
- #else
- using std::hypot; T h = hypot(x, y);
- #endif
- 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 T sum(T u, T v, T& t);
- /**
- * 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 T polyval(int N, const T p[], T x) {
- // This used to employ Math::fma; but that's too slow and it seemed not to
- // improve the accuracy noticeably. This might change when there's direct
- // hardware support for fma.
- 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 T AngNormalize(T x) {
- using std::remainder;
- x = remainder(x, T(360)); return x != -180 ? x : 180;
- }
- /**
- * 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 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 T AngDiff(T x, T y, T& e) {
- using std::remainder;
- T t, d = AngNormalize(sum(remainder(-x, T(360)),
- remainder( y, T(360)), t));
- // 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);
- }
- /**
- * 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.
- * @return \e y − \e x, reduced to the range [−180°,
- * 180°].
- *
- * The result is equivalent to computing the difference exactly, reducing
- * it to (−180°, 180°] and rounding the result. Note that
- * this prescription allows −180° to be returned (e.g., if \e x
- * is tiny and negative and \e y = 180°).
- **********************************************************************/
- template<typename T> static T AngDiff(T x, T y)
- { T e; return AngDiff(x, y, 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 T AngRound(T x);
- /**
- * 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 void sincosd(T x, T& sinx, T& cosx);
- /**
- * Evaluate the sine function with the argument in degrees
- *
- * @tparam T the type of the argument and the returned value.
- * @param[in] x in degrees.
- * @return sin(<i>x</i>).
- **********************************************************************/
- template<typename T> static T sind(T x);
- /**
- * Evaluate the cosine function with the argument in degrees
- *
- * @tparam T the type of the argument and the returned value.
- * @param[in] x in degrees.
- * @return cos(<i>x</i>).
- **********************************************************************/
- template<typename T> static T cosd(T x);
- /**
- * Evaluate the tangent function with the argument in degrees
- *
- * @tparam T the type of the argument and the returned value.
- * @param[in] x in degrees.
- * @return tan(<i>x</i>).
- *
- * If \e x = ±90°, then a suitably large (but finite) value is
- * returned.
- **********************************************************************/
- template<typename T> static T tand(T x);
- /**
- * Evaluate the atan2 function with the result in degrees
- *
- * @tparam T the type of the arguments and the returned value.
- * @param[in] y
- * @param[in] x
- * @return atan2(<i>y</i>, <i>x</i>) in degrees.
- *
- * The result is in the range (−180° 180°]. N.B.,
- * atan2d(±0, −1) = +180°; atan2d(−ε,
- * −1) = −180°, for ε positive and tiny;
- * atan2d(±0, +1) = ±0°.
- **********************************************************************/
- template<typename T> static T atan2d(T y, T x);
- /**
- * Evaluate the atan function with the result in degrees
- *
- * @tparam T the type of the argument and the returned value.
- * @param[in] x
- * @return atan(<i>x</i>) in degrees.
- **********************************************************************/
- template<typename T> static T atand(T x);
- /**
- * 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 eatanhe(T x, T es);
- /**
- * tanχ in terms of tanφ
- *
- * @tparam T the type of the argument and the returned value.
- * @param[in] tau τ = tanφ
- * @param[in] es the signed eccentricity = sign(<i>e</i><sup>2</sup>)
- * sqrt(|<i>e</i><sup>2</sup>|)
- * @return τ′ = tanχ
- *
- * See Eqs. (7--9) of
- * C. F. F. Karney,
- * <a href="https://doi.org/10.1007/s00190-011-0445-3">
- * Transverse Mercator with an accuracy of a few nanometers,</a>
- * J. Geodesy 85(8), 475--485 (Aug. 2011)
- * (preprint
- * <a href="https://arxiv.org/abs/1002.1417">arXiv:1002.1417</a>).
- **********************************************************************/
- template<typename T> static T taupf(T tau, T es);
- /**
- * tanφ in terms of tanχ
- *
- * @tparam T the type of the argument and the returned value.
- * @param[in] taup τ′ = tanχ
- * @param[in] es the signed eccentricity = sign(<i>e</i><sup>2</sup>)
- * sqrt(|<i>e</i><sup>2</sup>|)
- * @return τ = tanφ
- *
- * See Eqs. (19--21) of
- * C. F. F. Karney,
- * <a href="https://doi.org/10.1007/s00190-011-0445-3">
- * Transverse Mercator with an accuracy of a few nanometers,</a>
- * J. Geodesy 85(8), 475--485 (Aug. 2011)
- * (preprint
- * <a href="https://arxiv.org/abs/1002.1417">arXiv:1002.1417</a>).
- **********************************************************************/
- template<typename T> static T tauf(T taup, T es);
- /**
- * Test for finiteness.
- *
- * @tparam T the type of the argument.
- * @param[in] x
- * @return true if number is finite, false if NaN or infinite.
- *
- * \deprecated Use std::isfinite(x).
- **********************************************************************/
- template<typename T>
- GEOGRAPHICLIB_DEPRECATED("Use std::isfinite(x)")
- static bool isfinite(T x);
- /**
- * 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 = real> static T NaN();
- /**
- * Test for NaN.
- *
- * @tparam T the type of the argument.
- * @param[in] x
- * @return true if argument is a NaN.
- *
- * \deprecated Use std::isnan(x).
- **********************************************************************/
- template<typename T>
- GEOGRAPHICLIB_DEPRECATED("Use std::isnan(x)")
- static bool isnan(T x);
- /**
- * Infinity
- *
- * @tparam T the type of the returned value.
- * @return infinity if available, otherwise return the max real.
- **********************************************************************/
- template<typename T = real> static T infinity();
- /**
- * Swap the bytes of a quantity
- *
- * @tparam T the type of the argument and the returned value.
- * @param[in] x
- * @return x with its bytes swapped.
- **********************************************************************/
- template<typename T> static T swab(T x) {
- union {
- T r;
- unsigned char c[sizeof(T)];
- } b;
- b.r = x;
- for (int i = sizeof(T)/2; i--; )
- std::swap(b.c[i], b.c[sizeof(T) - 1 - i]);
- return b.r;
- }
- };
- } // namespace GeographicLib
- #endif // GEOGRAPHICLIB_MATH_HPP
|