brintos

brintos / llvm-project-archived public Read only

0
0
Text · 3.6 KiB · 2b57920 Raw
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