xref: /aosp_15_r20/external/llvm-libc/src/math/generic/sincos_eval.h (revision 71db0c75aadcf003ffe3238005f61d7618a3fead)
1*71db0c75SAndroid Build Coastguard Worker //===-- Compute sin + cos for small angles ----------------------*- C++ -*-===//
2*71db0c75SAndroid Build Coastguard Worker //
3*71db0c75SAndroid Build Coastguard Worker // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*71db0c75SAndroid Build Coastguard Worker // See https://llvm.org/LICENSE.txt for license information.
5*71db0c75SAndroid Build Coastguard Worker // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*71db0c75SAndroid Build Coastguard Worker //
7*71db0c75SAndroid Build Coastguard Worker //===----------------------------------------------------------------------===//
8*71db0c75SAndroid Build Coastguard Worker 
9*71db0c75SAndroid Build Coastguard Worker #ifndef LLVM_LIBC_SRC_MATH_GENERIC_SINCOS_EVAL_H
10*71db0c75SAndroid Build Coastguard Worker #define LLVM_LIBC_SRC_MATH_GENERIC_SINCOS_EVAL_H
11*71db0c75SAndroid Build Coastguard Worker 
12*71db0c75SAndroid Build Coastguard Worker #include "src/__support/FPUtil/PolyEval.h"
13*71db0c75SAndroid Build Coastguard Worker #include "src/__support/FPUtil/double_double.h"
14*71db0c75SAndroid Build Coastguard Worker #include "src/__support/FPUtil/dyadic_float.h"
15*71db0c75SAndroid Build Coastguard Worker #include "src/__support/FPUtil/multiply_add.h"
16*71db0c75SAndroid Build Coastguard Worker #include "src/__support/integer_literals.h"
17*71db0c75SAndroid Build Coastguard Worker #include "src/__support/macros/config.h"
18*71db0c75SAndroid Build Coastguard Worker 
19*71db0c75SAndroid Build Coastguard Worker namespace LIBC_NAMESPACE_DECL {
20*71db0c75SAndroid Build Coastguard Worker 
21*71db0c75SAndroid Build Coastguard Worker namespace generic {
22*71db0c75SAndroid Build Coastguard Worker 
23*71db0c75SAndroid Build Coastguard Worker using fputil::DoubleDouble;
24*71db0c75SAndroid Build Coastguard Worker using Float128 = fputil::DyadicFloat<128>;
25*71db0c75SAndroid Build Coastguard Worker 
sincos_eval(const DoubleDouble & u,DoubleDouble & sin_u,DoubleDouble & cos_u)26*71db0c75SAndroid Build Coastguard Worker LIBC_INLINE double sincos_eval(const DoubleDouble &u, DoubleDouble &sin_u,
27*71db0c75SAndroid Build Coastguard Worker                                DoubleDouble &cos_u) {
28*71db0c75SAndroid Build Coastguard Worker   // Evaluate sin(y) = sin(x - k * (pi/128))
29*71db0c75SAndroid Build Coastguard Worker   // We use the degree-7 Taylor approximation:
30*71db0c75SAndroid Build Coastguard Worker   //   sin(y) ~ y - y^3/3! + y^5/5! - y^7/7!
31*71db0c75SAndroid Build Coastguard Worker   // Then the error is bounded by:
32*71db0c75SAndroid Build Coastguard Worker   //   |sin(y) - (y - y^3/3! + y^5/5! - y^7/7!)| < |y|^9/9! < 2^-54/9! < 2^-72.
33*71db0c75SAndroid Build Coastguard Worker   // For y ~ u_hi + u_lo, fully expanding the polynomial and drop any terms
34*71db0c75SAndroid Build Coastguard Worker   // < ulp(u_hi^3) gives us:
35*71db0c75SAndroid Build Coastguard Worker   //   y - y^3/3! + y^5/5! - y^7/7! = ...
36*71db0c75SAndroid Build Coastguard Worker   // ~ u_hi + u_hi^3 * (-1/6 + u_hi^2 * (1/120 - u_hi^2 * 1/5040)) +
37*71db0c75SAndroid Build Coastguard Worker   //        + u_lo (1 + u_hi^2 * (-1/2 + u_hi^2 / 24))
38*71db0c75SAndroid Build Coastguard Worker   double u_hi_sq = u.hi * u.hi; // Error < ulp(u_hi^2) < 2^(-6 - 52) = 2^-58.
39*71db0c75SAndroid Build Coastguard Worker   // p1 ~ 1/120 + u_hi^2 / 5040.
40*71db0c75SAndroid Build Coastguard Worker   double p1 = fputil::multiply_add(u_hi_sq, -0x1.a01a01a01a01ap-13,
41*71db0c75SAndroid Build Coastguard Worker                                    0x1.1111111111111p-7);
42*71db0c75SAndroid Build Coastguard Worker   // q1 ~ -1/2 + u_hi^2 / 24.
43*71db0c75SAndroid Build Coastguard Worker   double q1 = fputil::multiply_add(u_hi_sq, 0x1.5555555555555p-5, -0x1.0p-1);
44*71db0c75SAndroid Build Coastguard Worker   double u_hi_3 = u_hi_sq * u.hi;
45*71db0c75SAndroid Build Coastguard Worker   // p2 ~ -1/6 + u_hi^2 (1/120 - u_hi^2 * 1/5040)
46*71db0c75SAndroid Build Coastguard Worker   double p2 = fputil::multiply_add(u_hi_sq, p1, -0x1.5555555555555p-3);
47*71db0c75SAndroid Build Coastguard Worker   // q2 ~ 1 + u_hi^2 (-1/2 + u_hi^2 / 24)
48*71db0c75SAndroid Build Coastguard Worker   double q2 = fputil::multiply_add(u_hi_sq, q1, 1.0);
49*71db0c75SAndroid Build Coastguard Worker   double sin_lo = fputil::multiply_add(u_hi_3, p2, u.lo * q2);
50*71db0c75SAndroid Build Coastguard Worker   // Overall, |sin(y) - (u_hi + sin_lo)| < 2*ulp(u_hi^3) < 2^-69.
51*71db0c75SAndroid Build Coastguard Worker 
52*71db0c75SAndroid Build Coastguard Worker   // Evaluate cos(y) = cos(x - k * (pi/128))
53*71db0c75SAndroid Build Coastguard Worker   // We use the degree-8 Taylor approximation:
54*71db0c75SAndroid Build Coastguard Worker   //   cos(y) ~ 1 - y^2/2 + y^4/4! - y^6/6! + y^8/8!
55*71db0c75SAndroid Build Coastguard Worker   // Then the error is bounded by:
56*71db0c75SAndroid Build Coastguard Worker   //   |cos(y) - (...)| < |y|^10/10! < 2^-81
57*71db0c75SAndroid Build Coastguard Worker   // For y ~ u_hi + u_lo, fully expanding the polynomial and drop any terms
58*71db0c75SAndroid Build Coastguard Worker   // < ulp(u_hi^3) gives us:
59*71db0c75SAndroid Build Coastguard Worker   //   1 - y^2/2 + y^4/4! - y^6/6! + y^8/8! = ...
60*71db0c75SAndroid Build Coastguard Worker   // ~ 1 - u_hi^2/2 + u_hi^4(1/24 + u_hi^2 (-1/720 + u_hi^2/40320)) +
61*71db0c75SAndroid Build Coastguard Worker   //     + u_hi u_lo (-1 + u_hi^2/6)
62*71db0c75SAndroid Build Coastguard Worker   // We compute 1 - u_hi^2 accurately:
63*71db0c75SAndroid Build Coastguard Worker   //   v_hi + v_lo ~ 1 - u_hi^2/2
64*71db0c75SAndroid Build Coastguard Worker   // with error <= 2^-105.
65*71db0c75SAndroid Build Coastguard Worker   double u_hi_neg_half = (-0.5) * u.hi;
66*71db0c75SAndroid Build Coastguard Worker   DoubleDouble v;
67*71db0c75SAndroid Build Coastguard Worker 
68*71db0c75SAndroid Build Coastguard Worker #ifdef LIBC_TARGET_CPU_HAS_FMA
69*71db0c75SAndroid Build Coastguard Worker   v.hi = fputil::multiply_add(u.hi, u_hi_neg_half, 1.0);
70*71db0c75SAndroid Build Coastguard Worker   v.lo = 1.0 - v.hi; // Exact
71*71db0c75SAndroid Build Coastguard Worker   v.lo = fputil::multiply_add(u.hi, u_hi_neg_half, v.lo);
72*71db0c75SAndroid Build Coastguard Worker #else
73*71db0c75SAndroid Build Coastguard Worker   DoubleDouble u_hi_sq_neg_half = fputil::exact_mult(u.hi, u_hi_neg_half);
74*71db0c75SAndroid Build Coastguard Worker   v = fputil::exact_add(1.0, u_hi_sq_neg_half.hi);
75*71db0c75SAndroid Build Coastguard Worker   v.lo += u_hi_sq_neg_half.lo;
76*71db0c75SAndroid Build Coastguard Worker #endif // LIBC_TARGET_CPU_HAS_FMA
77*71db0c75SAndroid Build Coastguard Worker 
78*71db0c75SAndroid Build Coastguard Worker   // r1 ~ -1/720 + u_hi^2 / 40320
79*71db0c75SAndroid Build Coastguard Worker   double r1 = fputil::multiply_add(u_hi_sq, 0x1.a01a01a01a01ap-16,
80*71db0c75SAndroid Build Coastguard Worker                                    -0x1.6c16c16c16c17p-10);
81*71db0c75SAndroid Build Coastguard Worker   // s1 ~ -1 + u_hi^2 / 6
82*71db0c75SAndroid Build Coastguard Worker   double s1 = fputil::multiply_add(u_hi_sq, 0x1.5555555555555p-3, -1.0);
83*71db0c75SAndroid Build Coastguard Worker   double u_hi_4 = u_hi_sq * u_hi_sq;
84*71db0c75SAndroid Build Coastguard Worker   double u_hi_u_lo = u.hi * u.lo;
85*71db0c75SAndroid Build Coastguard Worker   // r2 ~ 1/24 + u_hi^2 (-1/720 + u_hi^2 / 40320)
86*71db0c75SAndroid Build Coastguard Worker   double r2 = fputil::multiply_add(u_hi_sq, r1, 0x1.5555555555555p-5);
87*71db0c75SAndroid Build Coastguard Worker   // s2 ~ v_lo + u_hi * u_lo * (-1 + u_hi^2 / 6)
88*71db0c75SAndroid Build Coastguard Worker   double s2 = fputil::multiply_add(u_hi_u_lo, s1, v.lo);
89*71db0c75SAndroid Build Coastguard Worker   double cos_lo = fputil::multiply_add(u_hi_4, r2, s2);
90*71db0c75SAndroid Build Coastguard Worker   // Overall, |cos(y) - (v_hi + cos_lo)| < 2*ulp(u_hi^4) < 2^-75.
91*71db0c75SAndroid Build Coastguard Worker 
92*71db0c75SAndroid Build Coastguard Worker   sin_u = fputil::exact_add(u.hi, sin_lo);
93*71db0c75SAndroid Build Coastguard Worker   cos_u = fputil::exact_add(v.hi, cos_lo);
94*71db0c75SAndroid Build Coastguard Worker 
95*71db0c75SAndroid Build Coastguard Worker   return fputil::multiply_add(fputil::FPBits<double>(u_hi_3).abs().get_val(),
96*71db0c75SAndroid Build Coastguard Worker                               0x1.0p-51, 0x1.0p-105);
97*71db0c75SAndroid Build Coastguard Worker }
98*71db0c75SAndroid Build Coastguard Worker 
sincos_eval(const Float128 & u,Float128 & sin_u,Float128 & cos_u)99*71db0c75SAndroid Build Coastguard Worker LIBC_INLINE void sincos_eval(const Float128 &u, Float128 &sin_u,
100*71db0c75SAndroid Build Coastguard Worker                              Float128 &cos_u) {
101*71db0c75SAndroid Build Coastguard Worker   Float128 u_sq = fputil::quick_mul(u, u);
102*71db0c75SAndroid Build Coastguard Worker 
103*71db0c75SAndroid Build Coastguard Worker   // sin(u) ~ x - x^3/3! + x^5/5! - x^7/7! + x^9/9! - x^11/11! + x^13/13!
104*71db0c75SAndroid Build Coastguard Worker   constexpr Float128 SIN_COEFFS[] = {
105*71db0c75SAndroid Build Coastguard Worker       {Sign::POS, -127, 0x80000000'00000000'00000000'00000000_u128}, // 1
106*71db0c75SAndroid Build Coastguard Worker       {Sign::NEG, -130, 0xaaaaaaaa'aaaaaaaa'aaaaaaaa'aaaaaaab_u128}, // -1/3!
107*71db0c75SAndroid Build Coastguard Worker       {Sign::POS, -134, 0x88888888'88888888'88888888'88888889_u128}, // 1/5!
108*71db0c75SAndroid Build Coastguard Worker       {Sign::NEG, -140, 0xd00d00d0'0d00d00d'00d00d00'd00d00d0_u128}, // -1/7!
109*71db0c75SAndroid Build Coastguard Worker       {Sign::POS, -146, 0xb8ef1d2a'b6399c7d'560e4472'800b8ef2_u128}, // 1/9!
110*71db0c75SAndroid Build Coastguard Worker       {Sign::NEG, -153, 0xd7322b3f'aa271c7f'3a3f25c1'bee38f10_u128}, // -1/11!
111*71db0c75SAndroid Build Coastguard Worker       {Sign::POS, -160, 0xb092309d'43684be5'1c198e91'd7b4269e_u128}, // 1/13!
112*71db0c75SAndroid Build Coastguard Worker   };
113*71db0c75SAndroid Build Coastguard Worker 
114*71db0c75SAndroid Build Coastguard Worker   // cos(u) ~ 1 - x^2/2 + x^4/4! - x^6/6! + x^8/8! - x^10/10! + x^12/12!
115*71db0c75SAndroid Build Coastguard Worker   constexpr Float128 COS_COEFFS[] = {
116*71db0c75SAndroid Build Coastguard Worker       {Sign::POS, -127, 0x80000000'00000000'00000000'00000000_u128}, // 1.0
117*71db0c75SAndroid Build Coastguard Worker       {Sign::NEG, -128, 0x80000000'00000000'00000000'00000000_u128}, // 1/2
118*71db0c75SAndroid Build Coastguard Worker       {Sign::POS, -132, 0xaaaaaaaa'aaaaaaaa'aaaaaaaa'aaaaaaab_u128}, // 1/4!
119*71db0c75SAndroid Build Coastguard Worker       {Sign::NEG, -137, 0xb60b60b6'0b60b60b'60b60b60'b60b60b6_u128}, // 1/6!
120*71db0c75SAndroid Build Coastguard Worker       {Sign::POS, -143, 0xd00d00d0'0d00d00d'00d00d00'd00d00d0_u128}, // 1/8!
121*71db0c75SAndroid Build Coastguard Worker       {Sign::NEG, -149, 0x93f27dbb'c4fae397'780b69f5'333c725b_u128}, // 1/10!
122*71db0c75SAndroid Build Coastguard Worker       {Sign::POS, -156, 0x8f76c77f'c6c4bdaa'26d4c3d6'7f425f60_u128}, // 1/12!
123*71db0c75SAndroid Build Coastguard Worker   };
124*71db0c75SAndroid Build Coastguard Worker 
125*71db0c75SAndroid Build Coastguard Worker   sin_u = fputil::quick_mul(u, fputil::polyeval(u_sq, SIN_COEFFS[0],
126*71db0c75SAndroid Build Coastguard Worker                                                 SIN_COEFFS[1], SIN_COEFFS[2],
127*71db0c75SAndroid Build Coastguard Worker                                                 SIN_COEFFS[3], SIN_COEFFS[4],
128*71db0c75SAndroid Build Coastguard Worker                                                 SIN_COEFFS[5], SIN_COEFFS[6]));
129*71db0c75SAndroid Build Coastguard Worker   cos_u = fputil::polyeval(u_sq, COS_COEFFS[0], COS_COEFFS[1], COS_COEFFS[2],
130*71db0c75SAndroid Build Coastguard Worker                            COS_COEFFS[3], COS_COEFFS[4], COS_COEFFS[5],
131*71db0c75SAndroid Build Coastguard Worker                            COS_COEFFS[6]);
132*71db0c75SAndroid Build Coastguard Worker }
133*71db0c75SAndroid Build Coastguard Worker 
134*71db0c75SAndroid Build Coastguard Worker } // namespace generic
135*71db0c75SAndroid Build Coastguard Worker 
136*71db0c75SAndroid Build Coastguard Worker } // namespace LIBC_NAMESPACE_DECL
137*71db0c75SAndroid Build Coastguard Worker 
138*71db0c75SAndroid Build Coastguard Worker #endif // LLVM_LIBC_SRC_MATH_GENERIC_SINCOSF_EVAL_H
139