Skip to content

[libc][math][c23] Add sinf16 C23 math function #116674

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Dec 3, 2024

Conversation

wldfngrs
Copy link
Contributor

@wldfngrs wldfngrs commented Nov 18, 2024

  • Implementation of sin for 16-bit floating point inputs
  • Exhaustive tests for sinf16()

Copy link

github-actions bot commented Nov 18, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@wldfngrs wldfngrs marked this pull request as ready for review November 22, 2024 19:50
@llvmbot llvmbot added the libc label Nov 22, 2024
@llvmbot
Copy link
Member

llvmbot commented Nov 22, 2024

@llvm/pr-subscribers-libc

Author: wldfngrs (wldfngrs)

Changes

Sin for 16-bit floating point input


Full diff: https://github.com/llvm/llvm-project/pull/116674.diff

12 Files Affected:

  • (modified) libc/config/linux/x86_64/entrypoints.txt (+1)
  • (modified) libc/newhdrgen/yaml/math.yaml (+7)
  • (modified) libc/src/math/CMakeLists.txt (+1)
  • (modified) libc/src/math/generic/CMakeLists.txt (+20)
  • (modified) libc/src/math/generic/sincosf16_utils.h (+28-4)
  • (added) libc/src/math/generic/sinf16.cpp (+106)
  • (modified) libc/src/math/generic/tanpif16.cpp (+2-2)
  • (added) libc/src/math/sinf16.h (+21)
  • (modified) libc/test/src/math/CMakeLists.txt (+11)
  • (added) libc/test/src/math/sinf16_test.cpp (+40)
  • (modified) libc/test/src/math/smoke/CMakeLists.txt (+11)
  • (added) libc/test/src/math/smoke/sinf16_test.cpp (+33)
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 957e28bd66cc4c..99f4e34262a8df 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -699,6 +699,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
     libc.src.math.scalbnf16
     libc.src.math.setpayloadf16
     libc.src.math.setpayloadsigf16
+    libc.src.math.sinf16
     libc.src.math.sinhf16
     libc.src.math.sinpif16
     libc.src.math.sqrtf16
diff --git a/libc/newhdrgen/yaml/math.yaml b/libc/newhdrgen/yaml/math.yaml
index e09f0929e45f80..00efc34789667e 100644
--- a/libc/newhdrgen/yaml/math.yaml
+++ b/libc/newhdrgen/yaml/math.yaml
@@ -2339,6 +2339,13 @@ functions:
     return_type: float
     arguments:
       - type: float
+  - name: sinf16
+    standards:
+      - stdc
+    return_type: _Float16
+    arguments:
+      - type: _Float16
+    guard: LIBC_TYPES_HAS_FLOAT16
   - name: sinhf
     standards:
       - stdc
diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt
index 76a5e491effa05..390a59d07a28bc 100644
--- a/libc/src/math/CMakeLists.txt
+++ b/libc/src/math/CMakeLists.txt
@@ -484,6 +484,7 @@ add_math_entrypoint_object(sincosf)
 
 add_math_entrypoint_object(sin)
 add_math_entrypoint_object(sinf)
+add_math_entrypoint_object(sinf16)
 add_math_entrypoint_object(sinpif)
 add_math_entrypoint_object(sinpif16)
 
diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt
index 34b0f1424e8fd3..97a6784ef3c5f1 100644
--- a/libc/src/math/generic/CMakeLists.txt
+++ b/libc/src/math/generic/CMakeLists.txt
@@ -496,6 +496,26 @@ add_entrypoint_object(
     -O3
 )
 
+add_entrypoint_object(
+  sinf16
+  SRCS
+    sinf16.cpp
+  HDRS
+    ../sinf16.h
+  DEPENDS
+    .sincosf16_utils
+    libc.hdr.errno_macros
+    libc.hdr.fenv_macros
+    libc.src.__support.FPUtil.cast
+    libc.src.__support.FPUtil.fenv_impl
+    libc.src.__support.FPUtil.fp_bits
+    libc.src.__support.FPUtil.except_value_utils
+    libc.src.__support.FPUtil.multiply_add
+    libc.src.__support.macros.optimization
+  COMPILE_OPTIONS
+    -O3
+)
+
 add_entrypoint_object(
   sincos
   SRCS
diff --git a/libc/src/math/generic/sincosf16_utils.h b/libc/src/math/generic/sincosf16_utils.h
index 83511755a56c42..13d27fb9828a3d 100644
--- a/libc/src/math/generic/sincosf16_utils.h
+++ b/libc/src/math/generic/sincosf16_utils.h
@@ -11,6 +11,7 @@
 
 #include "src/__support/FPUtil/FPBits.h"
 #include "src/__support/FPUtil/PolyEval.h"
+#include "src/__support/FPUtil/cast.h"
 #include "src/__support/FPUtil/nearest_integer.h"
 #include "src/__support/common.h"
 #include "src/__support/macros/config.h"
@@ -46,10 +47,17 @@ LIBC_INLINE int32_t range_reduction_sincospif16(float x, float &y) {
   return static_cast<int32_t>(kf);
 }
 
-LIBC_INLINE void sincospif16_eval(float xf, float &sin_k, float &cos_k,
-                                  float &sin_y, float &cosm1_y) {
-  float y;
-  int32_t k = range_reduction_sincospif16(xf, y);
+LIBC_INLINE int32_t range_reduction_sincosf16(float x, float &y) {
+  double prod = x * 0x1.45f306dc9c883p3;
+  double kf = fputil::nearest_integer(prod);
+  y = static_cast<float>(prod - kf);
+
+  return static_cast<int32_t>(kf);
+}
+
+static LIBC_INLINE void sincosf16_poly_eval(int32_t k, float y, float &sin_k,
+                                            float &cos_k, float &sin_y,
+                                            float &cosm1_y) {
 
   sin_k = SIN_K_PI_OVER_32[k & 63];
   cos_k = SIN_K_PI_OVER_32[(k + 16) & 63];
@@ -72,6 +80,22 @@ LIBC_INLINE void sincospif16_eval(float xf, float &sin_k, float &cos_k,
                                    0x1.a6f7a2p-29f);
 }
 
+LIBC_INLINE void sincosf16_eval(float xf, float &sin_k, float &cos_k,
+                                float &sin_y, float &cosm1_y) {
+  float y;
+  int32_t k = range_reduction_sincosf16(xf, y);
+
+  sincosf16_poly_eval(k, y, sin_k, cos_k, sin_y, cosm1_y);
+}
+
+LIBC_INLINE void sincospif16_eval(float xf, float &sin_k, float &cos_k,
+                                  float &sin_y, float &cosm1_y) {
+  float y;
+  int32_t k = range_reduction_sincospif16(xf, y);
+
+  sincosf16_poly_eval(k, y, sin_k, cos_k, sin_y, cosm1_y);
+}
+
 } // namespace LIBC_NAMESPACE_DECL
 
 #endif // LLVM_LIBC_SRC_MATH_GENERIC_SINCOSF16_UTILS_H
diff --git a/libc/src/math/generic/sinf16.cpp b/libc/src/math/generic/sinf16.cpp
new file mode 100644
index 00000000000000..f307b3ea330c5e
--- /dev/null
+++ b/libc/src/math/generic/sinf16.cpp
@@ -0,0 +1,106 @@
+//===-- Half-precision sin 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/sinf16.h"
+#include "hdr/errno_macros.h"
+#include "hdr/fenv_macros.h"
+#include "sincosf16_utils.h"
+#include "src/__support/FPUtil/FEnvImpl.h"
+#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/FPUtil/cast.h"
+#include "src/__support/FPUtil/except_value_utils.h"
+#include "src/__support/FPUtil/multiply_add.h"
+#include "src/__support/macros/optimization.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+constexpr size_t N_EXCEPTS = 2;
+
+constexpr fputil::ExceptValues<float16, N_EXCEPTS> SINF16_EXCEPTS{{
+    // (input, RZ output, RU offset, RD offset, RN offset)
+    {0x585c, 0x3ba3, 1, 0, 1},
+    {0x5cb0, 0xbbff, 0, 1, 0},
+}};
+
+LLVM_LIBC_FUNCTION(float16, sinf16, (float16 x)) {
+  using FPBits = typename fputil::FPBits<float16>;
+  FPBits xbits(x);
+
+  uint16_t x_u = xbits.uintval();
+  uint16_t x_abs = x_u & 0x7fff;
+  float xf = x;
+
+  // Range reduction:
+  // For !x| > pi/32, we perform range reduction as follows:
+  // Find k and y such that:
+  //   x = (k + y) * pi/32
+  //   k is an integer, |y| < 0.5
+  //
+  // This is done by performing:
+  //   k = round(x * 32/pi)
+  //   y = x * 32/pi - k
+  //
+  // Once k and y are computed, we then deduce the answer by the sine of sum
+  // formula:
+  //   sin(x) = sin((k + y) * pi/32)
+  //   	      = sin(k * pi/32) * cos(y * pi/32) +
+  //   	        sin(y * pi/32) * cos(k * pi/32)
+
+  // Handle exceptional values
+  if (LIBC_UNLIKELY(x_abs == 0x585C || x_abs == 0x5cb0)) {
+    bool x_sign = x_u >> 15;
+    if (auto r = SINF16_EXCEPTS.lookup_odd(x_abs, x_sign);
+        LIBC_UNLIKELY(r.has_value()))
+      return r.value();
+  }
+
+  int rounding = fputil::quick_get_round();
+  if (LIBC_UNLIKELY(x_abs <= 0x13d0)) {
+    if (LIBC_UNLIKELY(x_abs == 0U))
+      return x;
+
+    if ((rounding == FE_UPWARD && xbits.is_pos()) ||
+        (rounding == FE_DOWNWARD && xbits.is_neg()))
+      return x;
+
+    if (rounding == FE_UPWARD && xbits.is_neg()) {
+      x_u--;
+      return FPBits(x_u).get_val();
+    }
+  }
+
+  if (LIBC_UNLIKELY(x_abs == 0x2b45)) {
+    if (rounding == FE_DOWNWARD && xbits.is_neg()) {
+      x_u--;
+      return FPBits(x_u).get_val();
+    }
+  }
+
+  if (LIBC_UNLIKELY(x_abs >= 0x7c00)) {
+    // If value is equal to infinity
+    if (x_abs == 0x7c00) {
+      fputil::set_errno_if_required(EDOM);
+      fputil::raise_except_if_required(FE_INVALID);
+    }
+
+    return x + FPBits::quiet_nan().get_val();
+  }
+
+  float sin_k, cos_k, sin_y, cosm1_y;
+  sincosf16_eval(xf, sin_k, cos_k, sin_y, cosm1_y);
+
+  if (LIBC_UNLIKELY(sin_y == 0 && sin_k == 0))
+    return FPBits::zero(xbits.sign()).get_val();
+
+  // Since, cosm1_y = cos_y - 1, therfore:
+  //    sin(x) = cos_k * sin_y + sin_k + (cosm1_y * sin_k)
+  return fputil::cast<float16>(fputil::multiply_add(
+      sin_y, cos_k, fputil::multiply_add(cosm1_y, sin_k, sin_k)));
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/math/generic/tanpif16.cpp b/libc/src/math/generic/tanpif16.cpp
index ab3c9cb2122ba9..67635536ee3193 100644
--- a/libc/src/math/generic/tanpif16.cpp
+++ b/libc/src/math/generic/tanpif16.cpp
@@ -21,7 +21,7 @@ namespace LIBC_NAMESPACE_DECL {
 
 constexpr size_t N_EXCEPTS = 21;
 
-constexpr fputil::ExceptValues<float16, N_EXCEPTS> TANF16_EXCEPTS{{
+constexpr fputil::ExceptValues<float16, N_EXCEPTS> TANPIF16_EXCEPTS{{
     // (input, RZ output, RU offset, RD offset, RN offset)
     {0x07f2, 0x0e3d, 1, 0, 0}, {0x086a, 0x0eee, 1, 0, 1},
     {0x08db, 0x0fa0, 1, 0, 0}, {0x094c, 0x1029, 1, 0, 0},
@@ -49,7 +49,7 @@ LLVM_LIBC_FUNCTION(float16, tanpif16, (float16 x)) {
       return x;
 
     bool x_sign = x_u >> 15;
-    if (auto r = TANF16_EXCEPTS.lookup_odd(x_abs, x_sign);
+    if (auto r = TANPIF16_EXCEPTS.lookup_odd(x_abs, x_sign);
         LIBC_UNLIKELY(r.has_value()))
       return r.value();
   }
diff --git a/libc/src/math/sinf16.h b/libc/src/math/sinf16.h
new file mode 100644
index 00000000000000..23f1aa99b62337
--- /dev/null
+++ b/libc/src/math/sinf16.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for sinf16 ------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_MATH_SINF16_H
+#define LLVM_LIBC_SRC_MATH_SINF16_H
+
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/properties/types.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+float16 sinf16(float16 x);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_MATH_SINF16_H
diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt
index 610f4d9fc1a3b9..ea75720df4f430 100644
--- a/libc/test/src/math/CMakeLists.txt
+++ b/libc/test/src/math/CMakeLists.txt
@@ -85,6 +85,17 @@ add_fp_unittest(
     libc.src.__support.FPUtil.fp_bits
 )
 
+add_fp_unittest(
+  sinf16_test
+  NEED_MPFR
+  SUITE
+    libc-math-unittests
+  SRCS
+    sinf16_test.cpp
+  DEPENDS
+    libc.src.math.sinf16  
+)
+
 add_fp_unittest(
   sinpif_test
   NEED_MPFR
diff --git a/libc/test/src/math/sinf16_test.cpp b/libc/test/src/math/sinf16_test.cpp
new file mode 100644
index 00000000000000..b469e1dcb8d33f
--- /dev/null
+++ b/libc/test/src/math/sinf16_test.cpp
@@ -0,0 +1,40 @@
+//===-- Exhaustive test for sinf16 ----------------------------------------===//
+//
+// 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/sinf16.h"
+#include "test/UnitTest/FPMatcher.h"
+#include "test/UnitTest/Test.h"
+#include "utils/MPFRWrapper/MPFRUtils.h"
+
+using LlvmLibcSinf16Test = LIBC_NAMESPACE::testing::FPTest<float16>;
+
+namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
+
+// Range: [0, Inf]
+static constexpr uint16_t POS_START = 0x0000U;
+static constexpr uint16_t POS_STOP = 0x7c00U;
+
+// Range: [-Inf, 0]
+static constexpr uint16_t NEG_START = 0x8000U;
+static constexpr uint16_t NEG_STOP = 0xfc00U;
+
+TEST_F(LlvmLibcSinf16Test, PositiveRange) {
+  for (uint16_t v = POS_START; v <= POS_STOP; ++v) {
+    float16 x = FPBits(v).get_val();
+    EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Sin, x,
+                                   LIBC_NAMESPACE::sinf16(x), 0.5);
+  }
+}
+
+TEST_F(LlvmLibcSinf16Test, NegativeRange) {
+  for (uint16_t v = NEG_START; v <= NEG_STOP; ++v) {
+    float16 x = FPBits(v).get_val();
+    EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Sin, x,
+                                   LIBC_NAMESPACE::sinf16(x), 0.5);
+  }
+}
diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt
index e9c785f7d93300..2c1c4dba738465 100644
--- a/libc/test/src/math/smoke/CMakeLists.txt
+++ b/libc/test/src/math/smoke/CMakeLists.txt
@@ -49,6 +49,17 @@ add_fp_unittest(
     libc.src.__support.FPUtil.fp_bits
 )
 
+add_fp_unittest(
+  sinf16_test
+  SUITE
+    libc-math-smoke-tests
+  SRCS
+    sinf16_test.cpp
+  DEPENDS
+    libc.src.errno.errno
+    libc.src.math.sinf16
+)
+
 add_fp_unittest(
   sinpif_test
   SUITE
diff --git a/libc/test/src/math/smoke/sinf16_test.cpp b/libc/test/src/math/smoke/sinf16_test.cpp
new file mode 100644
index 00000000000000..2966c3c952fd21
--- /dev/null
+++ b/libc/test/src/math/smoke/sinf16_test.cpp
@@ -0,0 +1,33 @@
+//===-- Unittests for sinf16 ----------------------------------------------===//
+//
+// 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/errno/libc_errno.h"
+#include "src/math/sinf16.h"
+#include "test/UnitTest/FPMatcher.h"
+#include "test/UnitTest/Test.h"
+
+using LlvmLibcSinf16Test = LIBC_NAMESPACE::testing::FPTest<float16>;
+
+TEST_F(LlvmLibcSinf16Test, SpecialNumbers) {
+  LIBC_NAMESPACE::libc_errno = 0;
+
+  EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::sinf16(aNaN));
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ(zero, LIBC_NAMESPACE::sinf16(zero));
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ(neg_zero, LIBC_NAMESPACE::sinf16(neg_zero));
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::sinf16(inf));
+  EXPECT_MATH_ERRNO(EDOM);
+
+  EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::sinf16(neg_inf));
+  EXPECT_MATH_ERRNO(EDOM);
+}

@wldfngrs wldfngrs marked this pull request as draft November 22, 2024 21:34
@wldfngrs wldfngrs marked this pull request as ready for review November 22, 2024 23:38
@wldfngrs
Copy link
Contributor Author

@lntue please review

@overmighty overmighty self-requested a review November 23, 2024 23:35
Fixed comments

Co-authored-by: OverMighty <its.overmighty@gmail.com>
@overmighty overmighty changed the title Add sinf16 function [libc][math][c23] Add sinf16 C23 math function Dec 3, 2024
@overmighty overmighty merged commit cd04653 into llvm:main Dec 3, 2024
8 checks passed
@wldfngrs wldfngrs deleted the add_sinf16_function branch December 3, 2024 20:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants