Skip to content

[libc][math][c23] Add cosf16 function #118785

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 3 commits into from
Dec 15, 2024
Merged

Conversation

wldfngrs
Copy link
Contributor

@wldfngrs wldfngrs commented Dec 5, 2024

  • Implementation of cos for 16-bit floating point inputs
  • Exhaustive tests across the 16-bit input range

@llvmbot llvmbot added the libc label Dec 5, 2024
@llvmbot
Copy link
Member

llvmbot commented Dec 5, 2024

@llvm/pr-subscribers-libc

Author: wldfngrs (wldfngrs)

Changes
  • Implementation of cos for 16-bit floating point inputs
  • Exhaustive tests across the 16-bit input range

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

13 Files Affected:

  • (modified) libc/config/linux/x86_64/entrypoints.txt (+1)
  • (modified) libc/docs/math/index.rst (+1-1)
  • (modified) libc/newhdrgen/yaml/math.yaml (+7)
  • (modified) libc/src/math/CMakeLists.txt (+1)
  • (added) libc/src/math/cosf16.h (+21)
  • (modified) libc/src/math/generic/CMakeLists.txt (+19)
  • (added) libc/src/math/generic/cosf16.cpp (+84)
  • (modified) libc/src/math/generic/cospif16.cpp (+1-1)
  • (modified) libc/src/math/generic/sinf16.cpp (+2-2)
  • (modified) libc/test/src/math/CMakeLists.txt (+11)
  • (added) libc/test/src/math/cosf16_test.cpp (+40)
  • (modified) libc/test/src/math/smoke/CMakeLists.txt (+11)
  • (added) libc/test/src/math/smoke/cosf16_test.cpp (+33)
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 5e9cc71279ab16..34366433b87c51 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -627,6 +627,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
     libc.src.math.canonicalizef16
     libc.src.math.ceilf16
     libc.src.math.copysignf16
+    libc.src.math.cosf16
     libc.src.math.coshf16
     libc.src.math.cospif16
     libc.src.math.exp10f16
diff --git a/libc/docs/math/index.rst b/libc/docs/math/index.rst
index 4934e93ccb1645..53c4e8ed2962ce 100644
--- a/libc/docs/math/index.rst
+++ b/libc/docs/math/index.rst
@@ -276,7 +276,7 @@ Higher Math Functions
 +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
 | compoundn |                  |                 |                        |                      |                        | 7.12.7.2               | F.10.4.2                   |
 +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
-| cos       | |check|          | |check|         |                        |                      |                        | 7.12.4.5               | F.10.1.5                   |
+| cos       | |check|          | |check|         |                        | |check|              |                        | 7.12.4.5               | F.10.1.5                   |
 +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
 | cosh      | |check|          |                 |                        | |check|              |                        | 7.12.5.4               | F.10.2.4                   |
 +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
diff --git a/libc/newhdrgen/yaml/math.yaml b/libc/newhdrgen/yaml/math.yaml
index 00efc34789667e..3b8caec66bbfd2 100644
--- a/libc/newhdrgen/yaml/math.yaml
+++ b/libc/newhdrgen/yaml/math.yaml
@@ -200,6 +200,13 @@ functions:
     return_type: float
     arguments:
       - type: float
+  - name: cosf16
+    standards:
+      - stdc
+    return_type: _Float16
+    arguments:
+      - type: _Float16
+    guard: LIBC_TYPES_HAS_FLOAT16
   - name: coshf
     standards:
       - stdc
diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt
index 390a59d07a28bc..e4e2c49642f2d0 100644
--- a/libc/src/math/CMakeLists.txt
+++ b/libc/src/math/CMakeLists.txt
@@ -89,6 +89,7 @@ add_math_entrypoint_object(copysignf128)
 
 add_math_entrypoint_object(cos)
 add_math_entrypoint_object(cosf)
+add_math_entrypoint_object(cosf16)
 
 add_math_entrypoint_object(cosh)
 add_math_entrypoint_object(coshf)
diff --git a/libc/src/math/cosf16.h b/libc/src/math/cosf16.h
new file mode 100644
index 00000000000000..cc179a6e16c626
--- /dev/null
+++ b/libc/src/math/cosf16.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for cosf16 ------------------------*- 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_COSF16_H
+#define LLVM_LIBC_SRC_MATH_COSF16_H
+
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/properties/types.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+float16 cosf16(float16 x);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_MATH_COSF16_H
diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt
index aeb758d4a092da..b3d46129151974 100644
--- a/libc/src/math/generic/CMakeLists.txt
+++ b/libc/src/math/generic/CMakeLists.txt
@@ -418,6 +418,25 @@ add_entrypoint_object(
     ${libc_opt_high_flag}
 )
 
+add_entrypoint_object(
+  cosf16
+  SRCS
+    cosf16.cpp
+  HDRS
+    ../cosf16.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
+    libc.src.__support.macros.properties.types
+)
+
 add_entrypoint_object(
   cospif
   SRCS
diff --git a/libc/src/math/generic/cosf16.cpp b/libc/src/math/generic/cosf16.cpp
new file mode 100644
index 00000000000000..2fad3b010172c4
--- /dev/null
+++ b/libc/src/math/generic/cosf16.cpp
@@ -0,0 +1,84 @@
+//===-- Half-precision cos(x) 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/cosf16.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 = 4;
+
+constexpr fputil::ExceptValues<float16, N_EXCEPTS> COSF16_EXCEPTS{{
+    // (input, RZ output, RU offset, RD offset, RN offset)
+    {0x2b7c, 0x3bfc, 1, 0, 1},
+    {0x4ac1, 0x38b5, 1, 0, 0},
+    {0x5c49, 0xb8c6, 0, 1, 0},
+    {0x7acc, 0xa474, 0, 1, 0},
+}};
+
+LLVM_LIBC_FUNCTION(float16, cosf16, (float16 x)) {
+  using FPBits = 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 cosine of sum
+  // formula:
+  //   cos(x) = cos((k + y) * pi/32)
+  //          = cos(k * pi/32) * cos(y * pi/32) -
+  //            sin(k * pi/32) * sin(y * pi/32)
+
+  // Handle exceptional values
+
+  if (LIBC_UNLIKELY(x_abs == 0x2b7c || x_abs == 0x4ac1 || x_abs == 0x5c49 ||
+                    x_abs == 0x7acc)) {
+    if (auto r = COSF16_EXCEPTS.lookup(x_abs); LIBC_UNLIKELY(r.has_value()))
+      return r.value();
+  }
+
+  if (LIBC_UNLIKELY(x_abs == 0U))
+    return fputil::cast<float16>(1.0f);
+
+  if (xbits.is_inf_or_nan()) {
+    if (xbits.is_inf()) {
+      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);
+  // Since, cosm1_y = cos_y - 1, therefore:
+  //   cos(x) = cos_k * cosm1_y - sin_k * sin_y
+  return fputil::cast<float16>(fputil::multiply_add(
+      cos_k, cosm1_y, fputil::multiply_add(-sin_k, sin_y, cos_k)));
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/math/generic/cospif16.cpp b/libc/src/math/generic/cospif16.cpp
index 384b39ff8e2c44..ee74bdb4a36937 100644
--- a/libc/src/math/generic/cospif16.cpp
+++ b/libc/src/math/generic/cospif16.cpp
@@ -73,7 +73,7 @@ LLVM_LIBC_FUNCTION(float16, cospif16, (float16 x)) {
     return fputil::cast<float16>(0.0f);
 
   // Since, cosm1_y = cos_y - 1, therefore:
-  // 	cos(x * pi) = cos_k(cosm1_y) + cos_k - sin_k * sin_y
+  //   cos(x * pi) = cos_k(cosm1_y) + cos_k - sin_k * sin_y
   return fputil::cast<float16>(fputil::multiply_add(
       cos_k, cosm1_y, fputil::multiply_add(-sin_k, sin_y, cos_k)));
 }
diff --git a/libc/src/math/generic/sinf16.cpp b/libc/src/math/generic/sinf16.cpp
index 86546348ba7392..d5a4aa56063ae4 100644
--- a/libc/src/math/generic/sinf16.cpp
+++ b/libc/src/math/generic/sinf16.cpp
@@ -99,8 +99,8 @@ LLVM_LIBC_FUNCTION(float16, sinf16, (float16 x)) {
   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)
+  // Since, cosm1_y = cos_y - 1, therefore:
+  //   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)));
 }
diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt
index ea75720df4f430..44a29f0c48e400 100644
--- a/libc/test/src/math/CMakeLists.txt
+++ b/libc/test/src/math/CMakeLists.txt
@@ -28,6 +28,17 @@ add_fp_unittest(
     libc.src.__support.FPUtil.fp_bits
 )
 
+add_fp_unittest(
+  cosf16_test
+  NEED_MPFR
+  SUITE
+    libc-math-unittests
+  SRCS
+    cosf16_test.cpp
+  DEPENDS
+    libc.src.math.cosf16
+)
+
 add_fp_unittest(
   cospif_test
   NEED_MPFR
diff --git a/libc/test/src/math/cosf16_test.cpp b/libc/test/src/math/cosf16_test.cpp
new file mode 100644
index 00000000000000..9e4687f0325c49
--- /dev/null
+++ b/libc/test/src/math/cosf16_test.cpp
@@ -0,0 +1,40 @@
+//===-- Exhaustive test for cosf16 ----------------------------------------===//
+//
+// 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/cosf16.h"
+#include "test/UnitTest/FPMatcher.h"
+#include "test/UnitTest/Test.h"
+#include "utils/MPFRWrapper/MPFRUtils.h"
+
+using LlvmLibcCosf16Test = 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(LlvmLibcCosf16Test, PositiveRange) {
+  for (uint16_t v = POS_START; v <= POS_STOP; ++v) {
+    float16 x = FPBits(v).get_val();
+    EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Cos, x,
+                                   LIBC_NAMESPACE::cosf16(x), 0.5);
+  }
+}
+
+TEST_F(LlvmLibcCosf16Test, NegativeRange) {
+  for (uint16_t v = NEG_START; v <= NEG_STOP; ++v) {
+    float16 x = FPBits(v).get_val();
+    EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Cos, x,
+                                   LIBC_NAMESPACE::cosf16(x), 0.5);
+  }
+}
diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt
index 2c1c4dba738465..31f85a3ecfd27b 100644
--- a/libc/test/src/math/smoke/CMakeLists.txt
+++ b/libc/test/src/math/smoke/CMakeLists.txt
@@ -12,6 +12,17 @@ add_fp_unittest(
     libc.src.math.cosf
 )
 
+add_fp_unittest(
+  cosf16_test
+  SUITE
+    libc-math-smoke-tests
+  SRCS
+    cosf16_test.cpp
+  DEPENDS
+    libc.src.errno.errno
+    libc.src.math.cosf16
+)
+
 add_fp_unittest(
   cospif_test
   SUITE
diff --git a/libc/test/src/math/smoke/cosf16_test.cpp b/libc/test/src/math/smoke/cosf16_test.cpp
new file mode 100644
index 00000000000000..9a51d1015da340
--- /dev/null
+++ b/libc/test/src/math/smoke/cosf16_test.cpp
@@ -0,0 +1,33 @@
+//===-- Unittests for cosf16 ----------------------------------------------===//
+//
+// 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/cosf16.h"
+#include "test/UnitTest/FPMatcher.h"
+#include "test/UnitTest/Test.h"
+
+using LlvmLibcCosf16Test = LIBC_NAMESPACE::testing::FPTest<float16>;
+
+TEST_F(LlvmLibcCosf16Test, SpecialNumbers) {
+  LIBC_NAMESPACE::libc_errno = 0;
+
+  EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::cosf16(aNaN));
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::cosf16(zero));
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::cosf16(neg_zero));
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::cosf16(inf));
+  EXPECT_MATH_ERRNO(EDOM);
+
+  EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::cosf16(neg_inf));
+  EXPECT_MATH_ERRNO(EDOM);
+}

@wldfngrs wldfngrs force-pushed the add_cosf16_function branch from 950fc05 to b759c89 Compare December 5, 2024 11:02
@wldfngrs wldfngrs changed the title [libc][math][c23] Add cosf16 function [libc][math][C23] Add cosf16 function Dec 5, 2024
@wldfngrs wldfngrs changed the title [libc][math][C23] Add cosf16 function [libc][math][c23] Add cosf16 function Dec 5, 2024
@lntue lntue requested review from overmighty and lntue December 5, 2024 15:08
Copy link
Member

@overmighty overmighty left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay. Could you please update the PR and resolve the merge conflict?

@wldfngrs wldfngrs force-pushed the add_cosf16_function branch from b759c89 to dfb0352 Compare December 13, 2024 01:24
@overmighty
Copy link
Member

Do you want me to merge this for you?

@wldfngrs
Copy link
Contributor Author

wldfngrs commented Dec 13, 2024

Do you want me to merge this for you?

Yeah, that's fine.

@overmighty overmighty merged commit 6a865b6 into llvm:main Dec 15, 2024
12 checks passed
@wldfngrs wldfngrs deleted the add_cosf16_function branch December 16, 2024 14:29
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