117 lines · cpp
1//===-- Half-precision sin(x) function ------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "src/math/sinf16.h"10#include "hdr/errno_macros.h"11#include "hdr/fenv_macros.h"12#include "src/__support/FPUtil/FEnvImpl.h"13#include "src/__support/FPUtil/FPBits.h"14#include "src/__support/FPUtil/cast.h"15#include "src/__support/FPUtil/except_value_utils.h"16#include "src/__support/FPUtil/multiply_add.h"17#include "src/__support/macros/optimization.h"18#include "src/__support/math/sincosf16_utils.h"19 20namespace LIBC_NAMESPACE_DECL {21 22#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS23constexpr size_t N_EXCEPTS = 4;24 25constexpr fputil::ExceptValues<float16, N_EXCEPTS> SINF16_EXCEPTS{{26 // (input, RZ output, RU offset, RD offset, RN offset)27 {0x2b45, 0x2b43, 1, 0, 1},28 {0x585c, 0x3ba3, 1, 0, 1},29 {0x5cb0, 0xbbff, 0, 1, 0},30 {0x51f5, 0xb80f, 0, 1, 0},31}};32#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS33 34LLVM_LIBC_FUNCTION(float16, sinf16, (float16 x)) {35 using namespace sincosf16_internal;36 using FPBits = fputil::FPBits<float16>;37 FPBits xbits(x);38 39 uint16_t x_u = xbits.uintval();40 uint16_t x_abs = x_u & 0x7fff;41 float xf = x;42 43 // Range reduction:44 // For |x| > pi/32, we perform range reduction as follows:45 // Find k and y such that:46 // x = (k + y) * pi/3247 // k is an integer, |y| < 0.548 //49 // This is done by performing:50 // k = round(x * 32/pi)51 // y = x * 32/pi - k52 //53 // Once k and y are computed, we then deduce the answer by the sine of sum54 // formula:55 // sin(x) = sin((k + y) * pi/32)56 // = sin(k * pi/32) * cos(y * pi/32) +57 // sin(y * pi/32) * cos(k * pi/32)58 59#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS60 // Handle exceptional values61 bool x_sign = x_u >> 15;62 63 if (auto r = SINF16_EXCEPTS.lookup_odd(x_abs, x_sign);64 LIBC_UNLIKELY(r.has_value()))65 return r.value();66#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS67 68 int rounding = fputil::quick_get_round();69 70 // Exhaustive tests show that for |x| <= 0x1.f4p-11, 1ULP rounding errors71 // occur. To fix this, the following apply:72 if (LIBC_UNLIKELY(x_abs <= 0x13d0)) {73 // sin(+/-0) = +/-074 if (LIBC_UNLIKELY(x_abs == 0U))75 return x;76 77 // When x > 0, and rounding upward, sin(x) == x.78 // When x < 0, and rounding downward, sin(x) == x.79 if ((rounding == FE_UPWARD && xbits.is_pos()) ||80 (rounding == FE_DOWNWARD && xbits.is_neg()))81 return x;82 83 // When x < 0, and rounding upward, sin(x) == (x - 1ULP)84 if (rounding == FE_UPWARD && xbits.is_neg()) {85 x_u--;86 return FPBits(x_u).get_val();87 }88 }89 90 if (xbits.is_inf_or_nan()) {91 if (xbits.is_signaling_nan()) {92 fputil::raise_except_if_required(FE_INVALID);93 return FPBits::quiet_nan().get_val();94 }95 96 if (xbits.is_inf()) {97 fputil::set_errno_if_required(EDOM);98 fputil::raise_except_if_required(FE_INVALID);99 }100 101 return x + FPBits::quiet_nan().get_val();102 }103 104 float sin_k, cos_k, sin_y, cosm1_y;105 sincosf16_eval(xf, sin_k, cos_k, sin_y, cosm1_y);106 107 if (LIBC_UNLIKELY(sin_y == 0 && sin_k == 0))108 return FPBits::zero(xbits.sign()).get_val();109 110 // Since, cosm1_y = cos_y - 1, therefore:111 // sin(x) = cos_k * sin_y + sin_k + (cosm1_y * sin_k)112 return fputil::cast<float16>(fputil::multiply_add(113 sin_y, cos_k, fputil::multiply_add(cosm1_y, sin_k, sin_k)));114}115 116} // namespace LIBC_NAMESPACE_DECL117