File size: 13,857 Bytes
00df61d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 | //===-- 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 "hdr/fenv_macros.h"
#include "inv_trigf_utils.h"
#include "src/__support/FPUtil/FEnvImpl.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
#if defined(LIBC_MATH_HAS_SKIP_ACCURATE_PASS) && \
defined(LIBC_MATH_HAS_INTERMEDIATE_COMP_IN_FLOAT)
// We use float-float implementation to reduce size.
#include "src/math/generic/atan2f_float.h"
#else
namespace LIBC_NAMESPACE_DECL {
namespace {
#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
// 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] = {
{0.0, 0.0},
{-0x1.c934d86d23f1dp-60, 0x1.ff55bb72cfdeap-5},
{-0x1.cd37686760c17p-59, 0x1.fd5ba9aac2f6ep-4},
{0x1.347b0b4f881cap-58, 0x1.7b97b4bce5b02p-3},
{0x1.8ab6e3cf7afbdp-57, 0x1.f5b75f92c80ddp-3},
{-0x1.963a544b672d8p-57, 0x1.362773707ebccp-2},
{-0x1.c63aae6f6e918p-56, 0x1.6f61941e4def1p-2},
{-0x1.24dec1b50b7ffp-56, 0x1.a64eec3cc23fdp-2},
{0x1.a2b7f222f65e2p-56, 0x1.dac670561bb4fp-2},
{-0x1.d5b495f6349e6p-56, 0x1.0657e94db30dp-1},
{-0x1.928df287a668fp-58, 0x1.1e00babdefeb4p-1},
{0x1.1021137c71102p-55, 0x1.345f01cce37bbp-1},
{0x1.2419a87f2a458p-56, 0x1.4978fa3269ee1p-1},
{0x1.0028e4bc5e7cap-57, 0x1.5d58987169b18p-1},
{-0x1.8c34d25aadef6p-56, 0x1.700a7c5784634p-1},
{-0x1.bf76229d3b917p-56, 0x1.819d0b7158a4dp-1},
{0x1.1a62633145c07p-55, 0x1.921fb54442d18p-1},
};
// 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] = {
{0.0, 1.0}, // 1
{-0x1.5555555555555p-56, -0x1.5555555555555p-2}, // -1/3
{-0x1.999999999999ap-57, 0x1.999999999999ap-3}, // 1/5
{-0x1.2492492492492p-57, -0x1.2492492492492p-3}, // -1/7
{0x1.c71c71c71c71cp-58, 0x1.c71c71c71c71cp-4}, // 1/9
{0x1.745d1745d1746p-59, -0x1.745d1745d1746p-4}, // -1/11
{-0x1.3b13b13b13b14p-58, 0x1.3b13b13b13b14p-4}, // 1/13
{-0x1.1111111111111p-60, -0x1.1111111111111p-4}, // -1/15
{0x1.e1e1e1e1e1e1ep-61, 0x1.e1e1e1e1e1e1ep-5}, // 1/17
};
// 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) {
fputil::DoubleDouble r{0.0, 0.0};
constexpr double C = 0x1.0p33 + 1.0;
double t1 = C * a;
double t2 = a - t1;
r.hi = t1 + t2;
r.lo = a - r.hi;
return r;
}
// 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) {
fputil::DoubleDouble q;
double num_r, den_r;
if (idx != 0) {
// The following range reduction is accurate even without fma for
// 1/16 <= n/d <= 1.
// atan(n/d) - atan(idx/16) = atan((n/d - idx/16) / (1 + (n/d) * (idx/16)))
// = atan((n - d*(idx/16)) / (d + n*idx/16))
k_d *= 0x1.0p-4;
num_r = fputil::multiply_add(k_d, -den_d, num_d); // Exact
den_r = fputil::multiply_add(k_d, num_d, den_d); // Exact
q.hi = num_r / den_r;
} else {
// For 0 < n/d < 1/16, we just need to calculate the lower part of their
// quotient.
q.hi = q_d;
num_r = num_d;
den_r = den_d;
}
#ifdef LIBC_TARGET_CPU_HAS_FMA_DOUBLE
q.lo = fputil::multiply_add(q.hi, -den_r, num_r) / den_r;
#else
// Compute `(num_r - q.hi * den_r) / den_r` accurately without FMA
// instructions.
fputil::DoubleDouble q_hi_dd = split_d(q.hi);
double t1 = fputil::multiply_add(q_hi_dd.hi, -den_r, num_r); // Exact
double t2 = fputil::multiply_add(q_hi_dd.lo, -den_r, t1);
q.lo = t2 / den_r;
#endif // LIBC_TARGET_CPU_HAS_FMA_DOUBLE
// Taylor polynomial, evaluating using Horner's scheme:
// 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
// = x*(1 + x^2*(-1/3 + x^2*(1/5 + x^2*(-1/7 + x^2*(1/9 + x^2*
// *(-1/11 + x^2*(1/13 + x^2*(-1/15 + x^2 * 1/17))))))))
fputil::DoubleDouble q2 = fputil::quick_mult(q, q);
fputil::DoubleDouble p_dd =
fputil::polyeval(q2, COEFFS[0], COEFFS[1], COEFFS[2], COEFFS[3],
COEFFS[4], COEFFS[5], COEFFS[6], COEFFS[7], COEFFS[8]);
fputil::DoubleDouble r_dd =
fputil::add(const_term, fputil::multiply_add(q, p_dd, ATAN_I[idx]));
r_dd.hi *= final_sign;
r_dd.lo *= final_sign;
// Make sure the sum is normalized:
fputil::DoubleDouble rr = fputil::exact_add(r_dd.hi, r_dd.lo);
// Round to odd.
uint64_t rr_bits = cpp::bit_cast<uint64_t>(rr.hi);
if (LIBC_UNLIKELY(((rr_bits & 0xfff'ffff) == 0) && (rr.lo != 0.0))) {
Sign hi_sign = fputil::FPBits<double>(rr.hi).sign();
Sign lo_sign = fputil::FPBits<double>(rr.lo).sign();
if (hi_sign == lo_sign) {
++rr_bits;
} else if ((rr_bits & fputil::FPBits<double>::FRACTION_MASK) > 0) {
--rr_bits;
}
}
return static_cast<float>(cpp::bit_cast<double>(rr_bits));
}
#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS
} // 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)) {
using FPBits = typename fputil::FPBits<float>;
constexpr double IS_NEG[2] = {1.0, -1.0};
constexpr double PI = 0x1.921fb54442d18p1;
constexpr double PI_LO = 0x1.1a62633145c07p-53;
constexpr double PI_OVER_4 = 0x1.921fb54442d18p-1;
constexpr double PI_OVER_2 = 0x1.921fb54442d18p0;
constexpr double THREE_PI_OVER_4 = 0x1.2d97c7f3321d2p+1;
// Adjustment for constant term:
// CONST_ADJ[x_sign][y_sign][recip]
constexpr fputil::DoubleDouble CONST_ADJ[2][2][2] = {
{{{0.0, 0.0}, {-PI_LO / 2, -PI_OVER_2}},
{{-0.0, -0.0}, {-PI_LO / 2, -PI_OVER_2}}},
{{{-PI_LO, -PI}, {PI_LO / 2, PI_OVER_2}},
{{-PI_LO, -PI}, {PI_LO / 2, PI_OVER_2}}}};
FPBits x_bits(x), y_bits(y);
bool x_sign = x_bits.sign().is_neg();
bool y_sign = y_bits.sign().is_neg();
x_bits.set_sign(Sign::POS);
y_bits.set_sign(Sign::POS);
uint32_t x_abs = x_bits.uintval();
uint32_t y_abs = y_bits.uintval();
uint32_t max_abs = x_abs > y_abs ? x_abs : y_abs;
uint32_t min_abs = x_abs <= y_abs ? x_abs : y_abs;
float num_f = FPBits(min_abs).get_val();
float den_f = FPBits(max_abs).get_val();
double num_d = static_cast<double>(num_f);
double den_d = static_cast<double>(den_f);
if (LIBC_UNLIKELY(max_abs >= 0x7f80'0000U || num_d == 0.0)) {
if (x_bits.is_nan() || y_bits.is_nan()) {
if (x_bits.is_signaling_nan() || y_bits.is_signaling_nan())
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}
double x_d = static_cast<double>(x);
double y_d = static_cast<double>(y);
size_t x_except = (x_d == 0.0) ? 0 : (x_abs == 0x7f80'0000 ? 2 : 1);
size_t y_except = (y_d == 0.0) ? 0 : (y_abs == 0x7f80'0000 ? 2 : 1);
// Exceptional cases:
// EXCEPT[y_except][x_except][x_is_neg]
// with x_except & y_except:
// 0: zero
// 1: finite, non-zero
// 2: infinity
constexpr double EXCEPTS[3][3][2] = {
{{0.0, PI}, {0.0, PI}, {0.0, PI}},
{{PI_OVER_2, PI_OVER_2}, {0.0, 0.0}, {0.0, PI}},
{{PI_OVER_2, PI_OVER_2},
{PI_OVER_2, PI_OVER_2},
{PI_OVER_4, THREE_PI_OVER_4}},
};
double r = IS_NEG[y_sign] * EXCEPTS[y_except][x_except][x_sign];
return static_cast<float>(r);
}
bool recip = x_abs < y_abs;
double final_sign = IS_NEG[(x_sign != y_sign) != recip];
fputil::DoubleDouble const_term = CONST_ADJ[x_sign][y_sign][recip];
double q_d = num_d / den_d;
double k_d = fputil::nearest_integer(q_d * 0x1.0p4);
int idx = static_cast<int>(k_d);
double r;
#ifdef LIBC_MATH_HAS_SMALL_TABLES
double p = atan_eval_no_table(num_d, den_d, k_d * 0x1.0p-4);
r = final_sign * (p + (const_term.hi + ATAN_K_OVER_16[idx]));
#else
q_d = fputil::multiply_add(k_d, -0x1.0p-4, q_d);
double p = atan_eval(q_d, idx);
r = final_sign *
fputil::multiply_add(q_d, p, const_term.hi + ATAN_COEFFS[idx][0]);
#endif // LIBC_MATH_HAS_SMALL_TABLES
#ifdef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
return static_cast<float>(r);
#else
constexpr uint32_t LOWER_ERR = 4;
// Mask sticky bits in double precision before rounding to single precision.
constexpr uint32_t MASK =
mask_trailing_ones<uint32_t, fputil::FPBits<double>::SIG_LEN -
FPBits::SIG_LEN - 1>();
constexpr uint32_t UPPER_ERR = MASK - LOWER_ERR;
uint32_t r_bits = static_cast<uint32_t>(cpp::bit_cast<uint64_t>(r)) & MASK;
// Ziv's rounding test.
if (LIBC_LIKELY(r_bits > LOWER_ERR && r_bits < UPPER_ERR))
return static_cast<float>(r);
return atan2f_double_double(num_d, den_d, q_d, idx, k_d, final_sign,
const_term);
#endif // LIBC_MATH_HAS_SKIP_ACCURATE_PASS
}
} // namespace LIBC_NAMESPACE_DECL
#endif
|