[libc-commits] [libc] [libc][C23][math] Implement cospif function correctly rounded for all rounding modes (PR #97464)

Hendrik Hübner via libc-commits libc-commits at lists.llvm.org
Fri Jul 5 03:50:46 PDT 2024


https://github.com/HendrikHuebner updated https://github.com/llvm/llvm-project/pull/97464

>From d1ab5682d74355335bb52a900b264c8469a04dc8 Mon Sep 17 00:00:00 2001
From: hhuebner <hendrik.huebner18 at gmail.com>
Date: Mon, 1 Jul 2024 23:16:30 +0200
Subject: [PATCH 1/6] [libc][math][C23] Correct sinpif range reduction comment

---
 libc/src/math/generic/sinpif.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libc/src/math/generic/sinpif.cpp b/libc/src/math/generic/sinpif.cpp
index 662263c9fc43ec..05bdad3ab4d0e3 100644
--- a/libc/src/math/generic/sinpif.cpp
+++ b/libc/src/math/generic/sinpif.cpp
@@ -26,13 +26,13 @@ LLVM_LIBC_FUNCTION(float, sinpif, (float x)) {
   double xd = static_cast<double>(x);
 
   // Range reduction:
-  // For |x| > pi/32, we perform range reduction as follows:
+  // For |x| > 1/32, we perform range reduction as follows:
   // Find k and y such that:
   //   x = (k + y) * 1/32
   //   k is an integer
   //   |y| < 0.5
-  // For small range (|x| < 2^45 when FMA instructions are available, 2^22
-  // otherwise), this is done by performing:
+  //
+  // This is done by performing:
   //   k = round(x * 32)
   //   y = x * 32 - k
   //

>From 072b032fc0ded7d98fa50bd42c5315daa424c627 Mon Sep 17 00:00:00 2001
From: hhuebner <hendrik.huebner18 at gmail.com>
Date: Tue, 2 Jul 2024 21:18:03 +0200
Subject: [PATCH 2/6] [libc][C23][math] Implement cospif function correctly
 rounded for all rounding modes

---
 libc/config/darwin/arm/entrypoints.txt        |   1 +
 libc/config/linux/aarch64/entrypoints.txt     |   1 +
 libc/config/linux/riscv/entrypoints.txt       |   1 +
 libc/config/linux/x86_64/entrypoints.txt      |   1 +
 libc/src/math/CMakeLists.txt                  |   1 +
 libc/src/math/cospif.h                        |  18 +++
 libc/src/math/generic/CMakeLists.txt          |  17 +++
 libc/src/math/generic/cospif.cpp              |  91 ++++++++++++++
 libc/test/src/math/CMakeLists.txt             |  16 +++
 libc/test/src/math/cospif_test.cpp            | 114 ++++++++++++++++++
 libc/test/src/math/exhaustive/CMakeLists.txt  |  16 +++
 libc/test/src/math/exhaustive/cospif_test.cpp |  35 ++++++
 libc/test/src/math/smoke/CMakeLists.txt       |  13 ++
 libc/test/src/math/smoke/cospif_test.cpp      |  34 ++++++
 libc/utils/MPFRWrapper/MPFRUtils.cpp          |  19 +++
 libc/utils/MPFRWrapper/MPFRUtils.h            |   1 +
 16 files changed, 379 insertions(+)
 create mode 100644 libc/src/math/cospif.h
 create mode 100644 libc/src/math/generic/cospif.cpp
 create mode 100644 libc/test/src/math/cospif_test.cpp
 create mode 100644 libc/test/src/math/exhaustive/cospif_test.cpp
 create mode 100644 libc/test/src/math/smoke/cospif_test.cpp

diff --git a/libc/config/darwin/arm/entrypoints.txt b/libc/config/darwin/arm/entrypoints.txt
index cb4603c79c79c7..d32745c9c94673 100644
--- a/libc/config/darwin/arm/entrypoints.txt
+++ b/libc/config/darwin/arm/entrypoints.txt
@@ -132,6 +132,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.coshf
     libc.src.math.cos
     libc.src.math.cosf
+    libc.src.math.cospif
     libc.src.math.erff
     libc.src.math.exp
     libc.src.math.expf
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index ff35e8fffec197..79e880b1bec8e5 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -346,6 +346,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.cos
     libc.src.math.cosf
     libc.src.math.coshf
+    libc.src.math.cospif
     libc.src.math.erff
     libc.src.math.exp
     libc.src.math.exp10
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 51d85eed9ff16c..fb9e4e37638440 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -354,6 +354,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.cos
     libc.src.math.cosf
     libc.src.math.coshf
+    libc.src.math.cospif
     libc.src.math.erff
     libc.src.math.exp
     libc.src.math.exp10
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 3eefa129c97585..6a61b51d602426 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -371,6 +371,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.cos
     libc.src.math.cosf
     libc.src.math.coshf
+    libc.src.math.cospif
     libc.src.math.erff
     libc.src.math.exp
     libc.src.math.exp10
diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt
index 5b20913134fdf5..ce4f8f993526d5 100644
--- a/libc/src/math/CMakeLists.txt
+++ b/libc/src/math/CMakeLists.txt
@@ -81,6 +81,7 @@ add_math_entrypoint_object(cos)
 add_math_entrypoint_object(cosf)
 add_math_entrypoint_object(cosh)
 add_math_entrypoint_object(coshf)
+add_math_entrypoint_object(cospif)
 
 add_math_entrypoint_object(erf)
 add_math_entrypoint_object(erff)
diff --git a/libc/src/math/cospif.h b/libc/src/math/cospif.h
new file mode 100644
index 00000000000000..50935bc33e59dd
--- /dev/null
+++ b/libc/src/math/cospif.h
@@ -0,0 +1,18 @@
+//===-- Implementation header for cospif ------------------------*- 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_COSPIF_H
+#define LLVM_LIBC_SRC_MATH_COSPIF_H
+
+namespace LIBC_NAMESPACE {
+
+float cospif(float x);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_MATH_COSPIF_H
diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt
index d6ea8c54174b69..6ace14063bcb1c 100644
--- a/libc/src/math/generic/CMakeLists.txt
+++ b/libc/src/math/generic/CMakeLists.txt
@@ -217,6 +217,23 @@ add_entrypoint_object(
     -O3
 )
 
+add_entrypoint_object(
+  cospif
+  SRCS
+    cospif.cpp
+  HDRS
+    ../cospif.h
+  DEPENDS
+    .sincosf_utils
+    libc.src.__support.FPUtil.fenv_impl
+    libc.src.__support.FPUtil.fp_bits
+    libc.src.__support.FPUtil.fma
+    libc.src.__support.FPUtil.multiply_add
+    libc.src.__support.macros.optimization
+  COMPILE_OPTIONS
+    -O3
+)
+
 add_entrypoint_object(
   sin
   SRCS
diff --git a/libc/src/math/generic/cospif.cpp b/libc/src/math/generic/cospif.cpp
new file mode 100644
index 00000000000000..24404b1676cd70
--- /dev/null
+++ b/libc/src/math/generic/cospif.cpp
@@ -0,0 +1,91 @@
+//===-- Single-precision cospi 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/cospif.h"
+#include "sincosf_utils.h"
+#include "src/__support/FPUtil/FEnvImpl.h"
+#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/FPUtil/multiply_add.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/optimization.h"            // LIBC_UNLIKELY
+#include "src/__support/macros/properties/cpu_features.h" // LIBC_TARGET_CPU_HAS_FMA
+
+namespace LIBC_NAMESPACE {
+
+LLVM_LIBC_FUNCTION(float, cospif, (float x)) {
+  using FPBits = typename fputil::FPBits<float>;
+
+  FPBits xbits(x);
+  xbits.set_sign(Sign::POS);
+
+  uint32_t x_abs = xbits.uintval();
+  double xd = static_cast<double>(xbits.get_val());
+
+  // Range reduction:
+  // For |x| > 1/32, we perform range reduction as follows:
+  // Find k and y such that:
+  //   x = (k + y) * 1/32
+  //   k is an integer
+  //   |y| < 0.5
+  //
+  // This is done by performing:
+  //   k = round(x * 32)
+  //   y = x * 32 - 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(y*pi/32) * cos(k*pi/32) - sin(y*pi/32) * sin(k*pi/32)
+  // The values of sin(k*pi/32) and cos(k*pi/32) for k = 0..63 are precomputed
+  // and stored using a vector of 32 doubles. Sin(y*pi/32) and cos(y*pi/32) are
+  // computed using degree-7 and degree-6 minimax polynomials generated by
+  // Sollya respectively.
+
+  // The exhautive test passes for smaller values
+  if (LIBC_UNLIKELY(x_abs < 0x38A2'F984U)) {
+
+#if defined(LIBC_TARGET_CPU_HAS_FMA)
+    return fputil::multiply_add(xbits.get_val(), -0x1.0p-25f, 1.0f);
+#else
+    return static_cast<float>(fputil::multiply_add(xd, -0x1.0p-25, 1.0));
+#endif // LIBC_TARGET_CPU_HAS_FMA
+  }
+
+  // Numbers greater or equal to 2^23 are always integers or NaN
+  if (LIBC_UNLIKELY(x_abs >= 0x4B00'0000)) {
+
+    if (LIBC_UNLIKELY(x_abs < 0x4B80'0000)) {
+      return (x_abs & 0x1) ? -1.0f : 1.0f;
+    }
+
+    // x is inf or nan.
+    if (LIBC_UNLIKELY(x_abs >= 0x7f80'0000U)) {
+      if (x_abs == 0x7f80'0000U) {
+        fputil::set_errno_if_required(EDOM);
+        fputil::raise_except_if_required(FE_INVALID);
+      }
+      return x + FPBits::quiet_nan().get_val();
+    }
+
+    return 1.0f;
+  }
+
+  // Combine the results with the sine of sum formula:
+  //   cos(pi * x) = cos((k + y)*pi/32)
+  //          = cos(y*pi/32) * cos(k*pi/32) - sin(y*pi/32) * sin(k*pi/32)
+  //          = (cosm1_y + 1) * cos_k - sin_y * sin_k
+  //          = (cosm1_y * cos_k + cos_k) - sin_y * sin_k
+  double sin_k, cos_k, sin_y, cosm1_y;
+
+  sincospif_eval(xd, sin_k, cos_k, sin_y, cosm1_y);
+
+  return static_cast<float>(fputil::multiply_add(
+      sin_y, -sin_k, fputil::multiply_add(cosm1_y, cos_k, cos_k)));
+}
+
+} // namespace LIBC_NAMESPACE
diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt
index 637e6720400ffb..0ae7464e8aa401 100644
--- a/libc/test/src/math/CMakeLists.txt
+++ b/libc/test/src/math/CMakeLists.txt
@@ -28,6 +28,22 @@ add_fp_unittest(
     libc.src.__support.FPUtil.fp_bits
 )
 
+add_fp_unittest(
+  cospif_test
+  NEED_MPFR
+  SUITE
+    libc-math-unittests
+  SRCS
+    cospif_test.cpp
+  HDRS
+    sdcomp26094.h
+  DEPENDS
+    libc.src.errno.errno
+    libc.src.math.cospif
+    libc.src.__support.CPP.array
+    libc.src.__support.FPUtil.fp_bits
+)
+
 add_fp_unittest(
   sinf_test
   NEED_MPFR
diff --git a/libc/test/src/math/cospif_test.cpp b/libc/test/src/math/cospif_test.cpp
new file mode 100644
index 00000000000000..9dad730a7b9b1c
--- /dev/null
+++ b/libc/test/src/math/cospif_test.cpp
@@ -0,0 +1,114 @@
+//===-- Unittests for cospif ----------------------------------------------===//
+//
+// 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/cospif.h"
+#include "test/UnitTest/FPMatcher.h"
+#include "test/src/math/sdcomp26094.h"
+#include "utils/MPFRWrapper/MPFRUtils.h"
+
+#include <stdint.h>
+
+using LlvmLibcCospifTest = LIBC_NAMESPACE::testing::FPTest<float>;
+
+using LIBC_NAMESPACE::testing::SDCOMP26094_VALUES;
+
+namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
+
+TEST_F(LlvmLibcCospifTest, SpecialNumbers) {
+  LIBC_NAMESPACE::libc_errno = 0;
+
+  EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::cospif(aNaN));
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::cospif(0.0f));
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::cospif(-0.0f));
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::cospif(inf));
+  EXPECT_MATH_ERRNO(EDOM);
+
+  EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::cospif(neg_inf));
+  EXPECT_MATH_ERRNO(EDOM);
+}
+
+TEST_F(LlvmLibcCospifTest, SpecificBitPatterns) {
+  constexpr int N = 38;
+  constexpr uint32_t INPUTS[N] = {0x3f06'0a92U, // x = pi/6
+                                  0x3f3a'dc51U, // x = 0x1.75b8a2p-1f
+                                  0x3f49'0fdbU, // x = pi/4
+                                  0x3f86'0a92U, // x = pi/3
+                                  0x3fa7'832aU, // x = 0x1.4f0654p+0f
+                                  0x3fc9'0fdbU, // x = pi/2
+                                  0x4017'1973U, // x = 0x1.2e32e6p+1f
+                                  0x4049'0fdbU, // x = pi
+                                  0x4096'cbe4U, // x = 0x1.2d97c8p+2f
+                                  0x40c9'0fdbU, // x = 2*pi
+                                  0x433b'7490U, // x = 0x1.76e92p+7f
+                                  0x437c'e5f1U, // x = 0x1.f9cbe2p+7f
+                                  0x4619'9998U, // x = 0x1.33333p+13f
+                                  0x474d'246fU, // x = 0x1.9a48dep+15f
+                                  0x4afd'ece4U, // x = 0x1.fbd9c8p+22f
+                                  0x4c23'32e9U, // x = 0x1.4665d2p+25f
+                                  0x50a3'e87fU, // x = 0x1.47d0fep+34f
+                                  0x5239'47f6U, // x = 0x1.728fecp+37f
+                                  0x53b1'46a6U, // x = 0x1.628d4cp+40f
+                                  0x55ca'fb2aU, // x = 0x1.95f654p+44f
+                                  0x588e'f060U, // x = 0x1.1de0cp+50f
+                                  0x5c07'bcd0U, // x = 0x1.0f79ap+57f
+                                  0x5ebc'fddeU, // x = 0x1.79fbbcp+62f
+                                  0x5fa6'eba7U, // x = 0x1.4dd74ep+64f
+                                  0x61a4'0b40U, // x = 0x1.48168p+68f
+                                  0x6386'134eU, // x = 0x1.0c269cp+72f
+                                  0x6589'8498U, // x = 0x1.13093p+76f
+                                  0x6600'0001U, // x = 0x1.000002p+77f
+                                  0x664e'46e4U, // x = 0x1.9c8dc8p+77f
+                                  0x66b0'14aaU, // x = 0x1.602954p+78f
+                                  0x67a9'242bU, // x = 0x1.524856p+80f
+                                  0x6a19'76f1U, // x = 0x1.32ede2p+85f
+                                  0x6c55'da58U, // x = 0x1.abb4bp+89f
+                                  0x6f79'be45U, // x = 0x1.f37c8ap+95f
+                                  0x7276'69d4U, // x = 0x1.ecd3a8p+101f
+                                  0x7758'4625U, // x = 0x1.b08c4ap+111f
+                                  0xB8A2'F987U};
+
+  for (int i = 0; i < N; ++i) {
+    float x = FPBits(INPUTS[i]).get_val();
+    EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Cospi, x,
+                                   LIBC_NAMESPACE::cospif(x), 0.5);
+    EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Cospi, -x,
+                                   LIBC_NAMESPACE::cospif(-x), 0.5);
+  }
+}
+
+// For small values, sinpi(x) is pi * x.
+TEST_F(LlvmLibcCospifTest, SmallValues) {
+  float x = FPBits(0x1780'0000U).get_val();
+  EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Cospi, x,
+                                 LIBC_NAMESPACE::cospif(x), 0.5);
+
+  x = FPBits(0x0040'0000U).get_val();
+  EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Cospi, x,
+                                 LIBC_NAMESPACE::cospif(x), 0.5);
+}
+
+// SDCOMP-26094: check sinfpi in the cases for which the range reducer
+// returns values furthest beyond its nominal upper bound of pi/4.
+TEST_F(LlvmLibcCospifTest, SDCOMP_26094) {
+  for (uint32_t v : SDCOMP26094_VALUES) {
+    float x = FPBits((v)).get_val();
+    EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Cospi, x,
+                                   LIBC_NAMESPACE::cospif(x), 0.5);
+  }
+}
+
+// sinpi(-n) = -0.0
+// sinpi(+n) = +0.0
+TEST_F(LlvmLibcCospifTest, SignedZeros) {}
diff --git a/libc/test/src/math/exhaustive/CMakeLists.txt b/libc/test/src/math/exhaustive/CMakeLists.txt
index 412ca031d0e996..c5f75b51cbd9f6 100644
--- a/libc/test/src/math/exhaustive/CMakeLists.txt
+++ b/libc/test/src/math/exhaustive/CMakeLists.txt
@@ -74,6 +74,22 @@ add_fp_unittest(
     -lpthread
 )
 
+add_fp_unittest(
+  cospif_test
+  NO_RUN_POSTBUILD
+  NEED_MPFR
+  SUITE
+    libc_math_exhaustive_tests
+  SRCS
+    cospif_test.cpp
+  DEPENDS
+    .exhaustive_test
+    libc.src.math.cospif
+    libc.src.__support.FPUtil.fp_bits
+  LINK_LIBRARIES
+    -lpthread
+)
+
 add_fp_unittest(
   sincosf_test
   NO_RUN_POSTBUILD
diff --git a/libc/test/src/math/exhaustive/cospif_test.cpp b/libc/test/src/math/exhaustive/cospif_test.cpp
new file mode 100644
index 00000000000000..f768aa6240b2c8
--- /dev/null
+++ b/libc/test/src/math/exhaustive/cospif_test.cpp
@@ -0,0 +1,35 @@
+//===-- Exhaustive test for cospif ----------------------------------------===//
+//
+// 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 "exhaustive_test.h"
+#include "mpfr.h"
+#include "src/math/cospif.h"
+#include "utils/MPFRWrapper/MPFRUtils.h"
+#include <sys/types.h>
+
+namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
+
+using LlvmLibcCospifExhaustiveTest =
+    LlvmLibcUnaryOpExhaustiveMathTest<float, mpfr::Operation::Cospi,
+                                      LIBC_NAMESPACE::cospif>;
+
+static constexpr uint32_t POS_START = 0x0000'0000U;
+static constexpr uint32_t POS_STOP = 0x7f80'0000U;
+
+// Range: [0, Inf]
+TEST_F(LlvmLibcCospifExhaustiveTest, PostiveRange) {
+  test_full_range_all_roundings(POS_START, POS_STOP);
+}
+
+// Range: [-Inf, 0]
+static constexpr uint32_t NEG_START = 0xb000'0000U;
+static constexpr uint32_t NEG_STOP = 0xff80'0000U;
+
+TEST_F(LlvmLibcCospifExhaustiveTest, NegativeRange) {
+  test_full_range_all_roundings(NEG_START, NEG_STOP);
+}
diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt
index 1b269edaa24778..5a58b44e09afa4 100644
--- a/libc/test/src/math/smoke/CMakeLists.txt
+++ b/libc/test/src/math/smoke/CMakeLists.txt
@@ -14,6 +14,19 @@ add_fp_unittest(
     libc.src.__support.FPUtil.fp_bits
 )
 
+add_fp_unittest(
+  cospif_test
+  SUITE
+    libc-math-smoke-tests
+  SRCS
+    cospif_test.cpp
+  DEPENDS
+    libc.src.errno.errno
+    libc.src.math.cospif
+    libc.src.__support.CPP.array
+    libc.src.__support.FPUtil.fp_bits
+)
+
 add_fp_unittest(
   sinf_test
   SUITE
diff --git a/libc/test/src/math/smoke/cospif_test.cpp b/libc/test/src/math/smoke/cospif_test.cpp
new file mode 100644
index 00000000000000..cd3910c64cdd7d
--- /dev/null
+++ b/libc/test/src/math/smoke/cospif_test.cpp
@@ -0,0 +1,34 @@
+//===-- Unittests for cospif ----------------------------------------------===//
+//
+// 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/cospif.h"
+#include "test/UnitTest/FPMatcher.h"
+
+#include <stdint.h>
+
+using LlvmLibcCospifTest = LIBC_NAMESPACE::testing::FPTest<float>;
+
+TEST_F(LlvmLibcCospifTest, SpecialNumbers) {
+  LIBC_NAMESPACE::libc_errno = 0;
+
+  EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::cospif(aNaN));
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::cospif(0.0f));
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ(-0.0f, LIBC_NAMESPACE::cospif(-0.0f));
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::cospif(inf));
+  EXPECT_MATH_ERRNO(EDOM);
+
+  EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::cospif(neg_inf));
+  EXPECT_MATH_ERRNO(EDOM);
+}
\ No newline at end of file
diff --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp
index f0a653824bea29..9a5946059bd9a2 100644
--- a/libc/utils/MPFRWrapper/MPFRUtils.cpp
+++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp
@@ -239,6 +239,23 @@ class MPFRNumber {
     return result;
   }
 
+  MPFRNumber cospi() const {
+    MPFRNumber result(*this);
+
+#if MPFR_VERSION_MAJOR > 4 ||                                                  \
+    (MPFR_VERSION_MAJOR == 4 && MPFR_VERSION_MINOR >= 2)
+
+    mpfr_cospi(result.value, value, mpfr_rounding);
+#else
+    MPFRNumber value_pi(0.0, 1280);
+    mpfr_const_pi(value_pi.value, MPFR_RNDN);
+    mpfr_mul(value_pi.value, value_pi.value, value, MPFR_RNDN);
+    mpfr_cos(result.value, value_pi.value, mpfr_rounding);
+#endif
+
+    return result;
+  }
+
   MPFRNumber erf() const {
     MPFRNumber result(*this);
     mpfr_erf(result.value, value, mpfr_rounding);
@@ -675,6 +692,8 @@ unary_operation(Operation op, InputType input, unsigned int precision,
     return mpfrInput.cos();
   case Operation::Cosh:
     return mpfrInput.cosh();
+  case Operation::Cospi:
+    return mpfrInput.cospi();
   case Operation::Erf:
     return mpfrInput.erf();
   case Operation::Exp:
diff --git a/libc/utils/MPFRWrapper/MPFRUtils.h b/libc/utils/MPFRWrapper/MPFRUtils.h
index 213dc7a65c3bc3..002dc919396e72 100644
--- a/libc/utils/MPFRWrapper/MPFRUtils.h
+++ b/libc/utils/MPFRWrapper/MPFRUtils.h
@@ -34,6 +34,7 @@ enum class Operation : int {
   Ceil,
   Cos,
   Cosh,
+  Cospi,
   Erf,
   Exp,
   Exp2,

>From 10e5d9682fbda949a7fa7ad49c9855646b54a4dc Mon Sep 17 00:00:00 2001
From: hhuebner <hendrik.huebner18 at gmail.com>
Date: Tue, 2 Jul 2024 21:37:35 +0200
Subject: [PATCH 3/6] Fix includes and comment

---
 libc/docs/math/index.rst                      | 2 +-
 libc/src/math/generic/cospif.cpp              | 2 +-
 libc/test/src/math/cospif_test.cpp            | 2 --
 libc/test/src/math/exhaustive/cospif_test.cpp | 2 --
 libc/test/src/math/smoke/CMakeLists.txt       | 2 --
 5 files changed, 2 insertions(+), 8 deletions(-)

diff --git a/libc/docs/math/index.rst b/libc/docs/math/index.rst
index e4da3d42baf7ac..6c84b10122677b 100644
--- a/libc/docs/math/index.rst
+++ b/libc/docs/math/index.rst
@@ -274,7 +274,7 @@ Higher Math Functions
 +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
 | cosh      | |check|          |                 |                        |                      |                        | 7.12.5.4               | F.10.2.4                   |
 +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
-| cospi     |                  |                 |                        |                      |                        | 7.12.4.12              | F.10.1.12                  |
+| cospi     | |check|          |                 |                        |                      |                        | 7.12.4.12              | F.10.1.12                  |
 +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
 | dsqrt     | N/A              | N/A             |                        | N/A                  |                        | 7.12.14.6              | F.10.11                    |
 +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
diff --git a/libc/src/math/generic/cospif.cpp b/libc/src/math/generic/cospif.cpp
index 24404b1676cd70..707f76c0870741 100644
--- a/libc/src/math/generic/cospif.cpp
+++ b/libc/src/math/generic/cospif.cpp
@@ -39,7 +39,7 @@ LLVM_LIBC_FUNCTION(float, cospif, (float x)) {
   //
   // Once k and y are computed, we then deduce the answer by the cosine of sum
   // formula:
-  //   cos(x) = cos((k + y)*pi/32)
+  //   cospi(x) = cos((k + y)*pi/32)
   //          = cos(y*pi/32) * cos(k*pi/32) - sin(y*pi/32) * sin(k*pi/32)
   // The values of sin(k*pi/32) and cos(k*pi/32) for k = 0..63 are precomputed
   // and stored using a vector of 32 doubles. Sin(y*pi/32) and cos(y*pi/32) are
diff --git a/libc/test/src/math/cospif_test.cpp b/libc/test/src/math/cospif_test.cpp
index 9dad730a7b9b1c..8f34dc4a2f617e 100644
--- a/libc/test/src/math/cospif_test.cpp
+++ b/libc/test/src/math/cospif_test.cpp
@@ -12,8 +12,6 @@
 #include "test/src/math/sdcomp26094.h"
 #include "utils/MPFRWrapper/MPFRUtils.h"
 
-#include <stdint.h>
-
 using LlvmLibcCospifTest = LIBC_NAMESPACE::testing::FPTest<float>;
 
 using LIBC_NAMESPACE::testing::SDCOMP26094_VALUES;
diff --git a/libc/test/src/math/exhaustive/cospif_test.cpp b/libc/test/src/math/exhaustive/cospif_test.cpp
index f768aa6240b2c8..59077d59099377 100644
--- a/libc/test/src/math/exhaustive/cospif_test.cpp
+++ b/libc/test/src/math/exhaustive/cospif_test.cpp
@@ -7,10 +7,8 @@
 //===----------------------------------------------------------------------===//
 
 #include "exhaustive_test.h"
-#include "mpfr.h"
 #include "src/math/cospif.h"
 #include "utils/MPFRWrapper/MPFRUtils.h"
-#include <sys/types.h>
 
 namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
 
diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt
index 5a58b44e09afa4..72d04439e2d936 100644
--- a/libc/test/src/math/smoke/CMakeLists.txt
+++ b/libc/test/src/math/smoke/CMakeLists.txt
@@ -10,8 +10,6 @@ add_fp_unittest(
   DEPENDS
     libc.src.errno.errno
     libc.src.math.cosf
-    libc.src.__support.CPP.array
-    libc.src.__support.FPUtil.fp_bits
 )
 
 add_fp_unittest(

>From 2b2d1885f7e5bb1b1a68acae40dba29f752ba4ef Mon Sep 17 00:00:00 2001
From: hhuebner <hendrik.huebner18 at gmail.com>
Date: Tue, 2 Jul 2024 21:48:09 +0200
Subject: [PATCH 4/6] Fix smoke test

---
 libc/test/src/math/smoke/cospif_test.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libc/test/src/math/smoke/cospif_test.cpp b/libc/test/src/math/smoke/cospif_test.cpp
index cd3910c64cdd7d..8e3f784ba20c99 100644
--- a/libc/test/src/math/smoke/cospif_test.cpp
+++ b/libc/test/src/math/smoke/cospif_test.cpp
@@ -20,10 +20,10 @@ TEST_F(LlvmLibcCospifTest, SpecialNumbers) {
   EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::cospif(aNaN));
   EXPECT_MATH_ERRNO(0);
 
-  EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::cospif(0.0f));
+  EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::cospif(0.0f));
   EXPECT_MATH_ERRNO(0);
 
-  EXPECT_FP_EQ(-0.0f, LIBC_NAMESPACE::cospif(-0.0f));
+  EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::cospif(-0.0f));
   EXPECT_MATH_ERRNO(0);
 
   EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::cospif(inf));

>From 94012aa68711e981ecdf4cfecf575139e56e8deb Mon Sep 17 00:00:00 2001
From: hhuebner <hendrik.huebner18 at gmail.com>
Date: Wed, 3 Jul 2024 11:34:58 +0200
Subject: [PATCH 5/6] Add missing newline

---
 libc/test/src/math/smoke/cospif_test.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libc/test/src/math/smoke/cospif_test.cpp b/libc/test/src/math/smoke/cospif_test.cpp
index 8e3f784ba20c99..007c4c45e3b157 100644
--- a/libc/test/src/math/smoke/cospif_test.cpp
+++ b/libc/test/src/math/smoke/cospif_test.cpp
@@ -31,4 +31,4 @@ TEST_F(LlvmLibcCospifTest, SpecialNumbers) {
 
   EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::cospif(neg_inf));
   EXPECT_MATH_ERRNO(EDOM);
-}
\ No newline at end of file
+}

>From 9a019b96bbd8ef88fdeef2a9a99259f4b7c7d950 Mon Sep 17 00:00:00 2001
From: hhuebner <ge84pip at mytum.de>
Date: Fri, 5 Jul 2024 12:49:40 +0200
Subject: [PATCH 6/6] Handle signed zeros

---
 libc/src/math/generic/cospif.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libc/src/math/generic/cospif.cpp b/libc/src/math/generic/cospif.cpp
index 707f76c0870741..b04e10fcc4d050 100644
--- a/libc/src/math/generic/cospif.cpp
+++ b/libc/src/math/generic/cospif.cpp
@@ -83,6 +83,9 @@ LLVM_LIBC_FUNCTION(float, cospif, (float x)) {
   double sin_k, cos_k, sin_y, cosm1_y;
 
   sincospif_eval(xd, sin_k, cos_k, sin_y, cosm1_y);
+  
+  if (LIBC_UNLIKELY(sin_y == 0 && cos_k == 0))
+    return FPBits::zero(xbits.sign()).get_val();
 
   return static_cast<float>(fputil::multiply_add(
       sin_y, -sin_k, fputil::multiply_add(cosm1_y, cos_k, cos_k)));



More information about the libc-commits mailing list