//===-- Single-precision atan2f function ----------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "src/math/atan2f.h" #include "inv_trigf_utils.h" #include "src/__support/FPUtil/FPBits.h" #include "src/__support/FPUtil/PolyEval.h" #include "src/__support/FPUtil/double_double.h" #include "src/__support/FPUtil/multiply_add.h" #include "src/__support/FPUtil/nearest_integer.h" #include "src/__support/FPUtil/rounding_mode.h" #include "src/__support/macros/config.h" #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY namespace LIBC_NAMESPACE_DECL { namespace { // Look up tables for accurate pass: // atan(i/16) with i = 0..16, generated by Sollya with: // > for i from 0 to 16 do { // a = round(atan(i/16), D, RN); // b = round(atan(i/16) - a, D, RN); // print("{", b, ",", a, "},"); // }; constexpr fputil::DoubleDouble ATAN_I[17] = …; // Taylor polynomial, generated by Sollya with: // > for i from 0 to 8 do { // j = (-1)^(i + 1)/(2*i + 1); // a = round(j, D, RN); // b = round(j - a, D, RN); // print("{", b, ",", a, "},"); // }; constexpr fputil::DoubleDouble COEFFS[9] = …; // Veltkamp's splitting of a double precision into hi + lo, where the hi part is // slightly smaller than an even split, so that the product of // hi * (s1 * k + s2) is exact, // where: // s1, s2 are single precsion, // 1/16 <= s1/s2 <= 1 // 1/16 <= k <= 1 is an integer. // So the maximal precision of (s1 * k + s2) is: // prec(s1 * k + s2) = 2 + log2(msb(s2)) - log2(lsb(k_d * s1)) // = 2 + log2(msb(s1)) + 4 - log2(lsb(k_d)) - log2(lsb(s1)) // = 2 + log2(lsb(s1)) + 23 + 4 - (-4) - log2(lsb(s1)) // = 33. // Thus, the Veltkamp splitting constant is C = 2^33 + 1. // This is used when FMA instruction is not available. [[maybe_unused]] constexpr fputil::DoubleDouble split_d(double a) { … } // Compute atan( num_d / den_d ) in double-double precision. // num_d = min(|x|, |y|) // den_d = max(|x|, |y|) // q_d = num_d / den_d // idx, k_d = round( 2^4 * num_d / den_d ) // final_sign = sign of the final result // const_term = the constant term in the final expression. float atan2f_double_double(double num_d, double den_d, double q_d, int idx, double k_d, double final_sign, const fputil::DoubleDouble &const_term) { … } } // anonymous namespace // There are several range reduction steps we can take for atan2(y, x) as // follow: // * Range reduction 1: signness // atan2(y, x) will return a number between -PI and PI representing the angle // forming by the 0x axis and the vector (x, y) on the 0xy-plane. // In particular, we have that: // atan2(y, x) = atan( y/x ) if x >= 0 and y >= 0 (I-quadrant) // = pi + atan( y/x ) if x < 0 and y >= 0 (II-quadrant) // = -pi + atan( y/x ) if x < 0 and y < 0 (III-quadrant) // = atan( y/x ) if x >= 0 and y < 0 (IV-quadrant) // Since atan function is odd, we can use the formula: // atan(-u) = -atan(u) // to adjust the above conditions a bit further: // atan2(y, x) = atan( |y|/|x| ) if x >= 0 and y >= 0 (I-quadrant) // = pi - atan( |y|/|x| ) if x < 0 and y >= 0 (II-quadrant) // = -pi + atan( |y|/|x| ) if x < 0 and y < 0 (III-quadrant) // = -atan( |y|/|x| ) if x >= 0 and y < 0 (IV-quadrant) // Which can be simplified to: // atan2(y, x) = sign(y) * atan( |y|/|x| ) if x >= 0 // = sign(y) * (pi - atan( |y|/|x| )) if x < 0 // * Range reduction 2: reciprocal // Now that the argument inside atan is positive, we can use the formula: // atan(1/x) = pi/2 - atan(x) // to make the argument inside atan <= 1 as follow: // atan2(y, x) = sign(y) * atan( |y|/|x|) if 0 <= |y| <= x // = sign(y) * (pi/2 - atan( |x|/|y| ) if 0 <= x < |y| // = sign(y) * (pi - atan( |y|/|x| )) if 0 <= |y| <= -x // = sign(y) * (pi/2 + atan( |x|/|y| )) if 0 <= -x < |y| // * Range reduction 3: look up table. // After the previous two range reduction steps, we reduce the problem to // compute atan(u) with 0 <= u <= 1, or to be precise: // atan( n / d ) where n = min(|x|, |y|) and d = max(|x|, |y|). // An accurate polynomial approximation for the whole [0, 1] input range will // require a very large degree. To make it more efficient, we reduce the input // range further by finding an integer idx such that: // | n/d - idx/16 | <= 1/32. // In particular, // idx := 2^-4 * round(2^4 * n/d) // Then for the fast pass, we find a polynomial approximation for: // atan( n/d ) ~ atan( idx/16 ) + (n/d - idx/16) * Q(n/d - idx/16) // For the accurate pass, we use the addition formula: // atan( n/d ) - atan( idx/16 ) = atan( (n/d - idx/16)/(1 + (n*idx)/(16*d)) ) // = atan( (n - d * idx/16)/(d + n * idx/16) ) // And finally we use Taylor polynomial to compute the RHS in the accurate pass: // atan(u) ~ P(u) = u - u^3/3 + u^5/5 - u^7/7 + u^9/9 - u^11/11 + u^13/13 - // - u^15/15 + u^17/17 // It's error in double-double precision is estimated in Sollya to be: // > P = x - x^3/3 + x^5/5 -x^7/7 + x^9/9 - x^11/11 + x^13/13 - x^15/15 // + x^17/17; // > dirtyinfnorm(atan(x) - P, [-2^-5, 2^-5]); // 0x1.aec6f...p-100 // which is about rounding errors of double-double (2^-104). LLVM_LIBC_FUNCTION(float, atan2f, (float y, float x)) { … } } // namespace LIBC_NAMESPACE_DECL