| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | //====================================================================================================================== | ||
| 2 | /* | ||
| 3 | POLYFLOAT - Extended precision floating points | ||
| 4 | Copyright : POLYFLOAT Contributors & Maintainers | ||
| 5 | SPDX-License-Identifier: BSL-1.0 | ||
| 6 | */ | ||
| 7 | //====================================================================================================================== | ||
| 8 | #pragma once | ||
| 9 | |||
| 10 | #include <polyfloat/details/callable.hpp> | ||
| 11 | #include <polyfloat/types/concepts.hpp> | ||
| 12 | #include <polyfloat/types/traits.hpp> | ||
| 13 | #include <type_traits> | ||
| 14 | #include <polyfloat/module/math/tan.hpp> | ||
| 15 | #include <polyfloat/module/math/atan.hpp> | ||
| 16 | #include <polyfloat/module/math/radindeg.hpp> | ||
| 17 | #include <polyfloat/module/math/radinpi.hpp> | ||
| 18 | |||
| 19 | namespace plf | ||
| 20 | { | ||
| 21 | |||
| 22 | template<typename Options> | ||
| 23 | struct atan2_t : plf::elementwise_callable<atan2_t, | ||
| 24 | eve::atan2_t, | ||
| 25 | Options, | ||
| 26 | raw_option, | ||
| 27 | pedantic_option, | ||
| 28 | eve::rad_option, | ||
| 29 | eve::radpi_option, | ||
| 30 | eve::deg_option> | ||
| 31 | { | ||
| 32 | template<concepts::polyfloat_like Z0, concepts::polyfloat_like Z1> | ||
| 33 | 20758 | POLYFLOAT_ABI constexpr plf::as_polyfloat_like_t<Z0, Z1> operator()(Z0 z0, Z1 z1) const noexcept | |
| 34 | { | ||
| 35 | 20758 | return POLYFLOAT_CALL(z0, z1); | |
| 36 | } | ||
| 37 | |||
| 38 | 20748 | POLYFLOAT_CALLABLE_OBJECT(atan2_t, atan2_); | |
| 39 | }; | ||
| 40 | //====================================================================================================================== | ||
| 41 | //! @addtogroup math | ||
| 42 | //! @{ | ||
| 43 | //! @var atan2 | ||
| 44 | //! @brief return the arctangent value. | ||
| 45 | //! | ||
| 46 | //! @groupheader{Header file} | ||
| 47 | //! | ||
| 48 | //! @code | ||
| 49 | //! #include <polyfloat/core.hpp> | ||
| 50 | //! @endcode | ||
| 51 | //! | ||
| 52 | //! @groupheader{Callable Signatures} | ||
| 53 | //! | ||
| 54 | //! @code | ||
| 55 | //! namespace polyfloat | ||
| 56 | //! { | ||
| 57 | //! template<polyfloat::concepts::polyfloat_like T> constexpr auto atan2(T z) noexcept; //1 | ||
| 58 | //! template<polyfloat::concepts::polyfloat_like T> constexpr auto atan2[pedantic](T z) noexcept; //2 | ||
| 59 | //! } | ||
| 60 | //! @endcode | ||
| 61 | //! | ||
| 62 | //! **Parameters** | ||
| 63 | //! | ||
| 64 | //! * `x, y`: Value to process. | ||
| 65 | //! | ||
| 66 | //! **Return value** | ||
| 67 | //! | ||
| 68 | //! 1. The arc tangent of \f$\frac{y}x\f$ in the radian range \f$[-\pi , +\pi]\f$ (1.a) , is returned. | ||
| 69 | //! (converted in degrees (1.b) or in \f$[\pi\f$ multiples (1.c)) | ||
| 70 | //! For 1.a The **IEEE** limiting values are almost all satisfied : | ||
| 71 | //! | ||
| 72 | //! * If `x` and `y` are both zero or infinites, Nan is returned (this is not standard conforming) | ||
| 73 | //! * If `y` is \f$\pm0\f$ and `x` is strictly negative or \f$-0\f$, \f$\pm\pi\f$ is returned | ||
| 74 | //! * If `y` is \f$\pm0\f$ and `x` is strictly positive or \f$+0\f$, \f$\pm0\f$ is returned | ||
| 75 | //! * If `y` is \f$\pm\infty\f$ and `x` is finite, \f$\pm\frac\pi2\f$ is returned | ||
| 76 | //! * If `x` is \f$\pm0\f$ and `y` is strictly negative, \f$-\frac\pi2\f$ is returned | ||
| 77 | //! * If `x` is \f$\pm0\f$ and `y` is strictly positive, \f$+\frac\pi2\f$ is returned | ||
| 78 | //! * If `x` is \f$-\infty\f$ and `y` is finite and positive, \f$+\pi\f$ is returned | ||
| 79 | //! * If `x` is \f$-\infty\f$ and `y` is finite and negative, \f$-\pi\f$ is returned | ||
| 80 | //! * If `x` is \f$+\infty\f$ and `y` is finite and positive, \f$+0\f$ is returned | ||
| 81 | //! * If `x` is \f$+\infty\f$ and `y` is finite and negative, \f$-0\f$ is returned | ||
| 82 | //! * If either `x` is Nan or `y` is Nan, Nan is returned | ||
| 83 | //! | ||
| 84 | //! The call will return a NaN if `x` and `y` are both either null or infinite: this result is | ||
| 85 | //! not **IEEE** conformant, but allows to simplify (and speed up) the implementation. | ||
| 86 | //! In all other cases, the result is standard conformant. And the converted equivalent are returned with the other units. | ||
| 87 | //! 2. Same as 1, except that all **IEEE** limiting values are satisfied : | ||
| 88 | //! * If `y` is \f$\pm\infty\f$ and `x` is \f$-\infty\f$, \f$\pm\frac{3\pi}4\f$ is returned | ||
| 89 | //! * If `y` is \f$\pm\infty\f$ and `x` is \f$+\infty\f$, \f$\pm\frac{\pi}4\f$ is returned | ||
| 90 | //! * If `x` is \f$\pm0\f$ and `y` is \f$\pm-0\f$, \f$-\frac\pi2\f$ is returned | ||
| 91 | //! * If `x` is \f$\pm0\f$ and `y` is \f$\pm+0\f$, \f$+\frac\pi2\f$ is returned | ||
| 92 | //! | ||
| 93 | //! @groupheader{Example} | ||
| 94 | //! | ||
| 95 | //! @godbolt{doc/math/atan2.cpp} | ||
| 96 | //====================================================================================================================== | ||
| 97 | |||
| 98 | inline constexpr auto atan2 = eve::functor<atan2_t>; | ||
| 99 | //====================================================================================================================== | ||
| 100 | //! @} | ||
| 101 | //====================================================================================================================== | ||
| 102 | } | ||
| 103 | |||
| 104 | namespace plf::_ | ||
| 105 | { | ||
| 106 | template<typename T, typename U, eve::callable_options O> | ||
| 107 | 20748 | constexpr auto atan2_(POLYFLOAT_DELAY(), O const& o, T a0, U a1) noexcept | |
| 108 | { | ||
| 109 | if constexpr (O::contains(eve::rad)) return atan2[o.drop(rad)](a0, a1); | ||
| 110 | else if constexpr (O::contains(eve::deg)) return radindeg(atan2[o.drop(eve::deg)](a0, a1)); | ||
| 111 | else if constexpr (O::contains(eve::radpi)) return radinpi(atan2[o.drop(eve::radpi)](a0, a1)); | ||
| 112 | else | ||
| 113 | { | ||
| 114 | using r_t = plf::as_polyfloat_like_t<T, U>; | ||
| 115 | using elt_t = eve::element_type_t<r_t>; | ||
| 116 | 20752 | auto cvt = [](auto a) { return plf::convert(a, eve::as<elt_t>()); }; | |
| 117 | 20748 | r_t a00 = cvt(a0); | |
| 118 | 20748 | r_t a10 = cvt(a1); | |
| 119 | if constexpr (O::contains(pedantic)) | ||
| 120 | { | ||
| 121 | if constexpr (eve::scalar_value<r_t> && eve::platform::supports_nans) | ||
| 122 | { | ||
| 123 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 32 times.
|
64 | if (plf::is_unordered(a00, a10)) return plf::nan(eve::as(a00)); |
| 124 | } | ||
| 125 | if constexpr (eve::platform::supports_infinites) | ||
| 126 | { | ||
| 127 | 64 | auto test1 = plf::is_infinite(a00) && plf::is_infinite(a10); | |
| 128 | 64 | a00 = plf::if_else(test1, plf::copysign(one(eve::as(a00)), a00), a00); | |
| 129 | 64 | a10 = plf::if_else(test1, plf::copysign(one(eve::as(a00)), a10), a10); | |
| 130 | } | ||
| 131 | 64 | auto q = plf::abs(a00 / a10); | |
| 132 | 64 | r_t z = plf::atan(q); | |
| 133 | 64 | r_t sgn = signnz(a00); | |
| 134 | 64 | z = plf::if_else(plf::is_positive(a10), z, plf::pi(eve::as(a0)) - z) * sgn; | |
| 135 | 64 | z = plf::if_else(plf::is_eqz(a00), plf::if_else(plf::is_negative(a10), plf::pi(eve::as(a00)) * sgn, plf::zero), | |
| 136 | z); | ||
| 137 | 64 | if constexpr (eve::platform::supports_nans) return plf::if_else(is_unordered(a00, a10), plf::allbits, z); | |
| 138 | else return z; | ||
| 139 | } | ||
| 140 | else | ||
| 141 | { | ||
| 142 | 20684 | auto q = plf::abs(a00 / a10); | |
| 143 | 20684 | auto z = plf::atan(q); | |
| 144 | 20684 | return if_else(is_positive(a10), z, (pi(eve::as(a00)) - z)) * signnz(a00); | |
| 145 | } | ||
| 146 | } | ||
| 147 | } | ||
| 148 | constexpr auto atan2d = plf::atan2[eve::deg]; | ||
| 149 | constexpr auto atan2pi = plf::atan2[eve::radpi]; | ||
| 150 | |||
| 151 | } | ||
| 152 |