Skip to content
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

[libc][C23][math] Implement cospif function correctly rounded for all rounding modes #97464

Merged
merged 2 commits into from
Jul 6, 2024

Conversation

HendrikHuebner
Copy link
Contributor

I also fixed a comment in sinpif.cpp in the first commit. Should this be included in this PR?

All tests were passed, including the exhaustive test.

CC: @lntue

@llvmbot llvmbot added the libc label Jul 2, 2024
@llvmbot
Copy link
Collaborator

llvmbot commented Jul 2, 2024

@llvm/pr-subscribers-libc

Author: Hendrik Hübner (HendrikHuebner)

Changes

I also fixed a comment in sinpif.cpp in the first commit. Should this be included in this PR?

All tests were passed, including the exhaustive test.

CC: @lntue


Patch is 20.44 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/97464.diff

18 Files Affected:

  • (modified) libc/config/darwin/arm/entrypoints.txt (+1)
  • (modified) libc/config/linux/aarch64/entrypoints.txt (+1)
  • (modified) libc/config/linux/riscv/entrypoints.txt (+1)
  • (modified) libc/config/linux/x86_64/entrypoints.txt (+1)
  • (modified) libc/docs/math/index.rst (+1-1)
  • (modified) libc/src/math/CMakeLists.txt (+1)
  • (added) libc/src/math/cospif.h (+18)
  • (modified) libc/src/math/generic/CMakeLists.txt (+17)
  • (added) libc/src/math/generic/cospif.cpp (+91)
  • (modified) libc/src/math/generic/sinpif.cpp (+3-3)
  • (modified) libc/test/src/math/CMakeLists.txt (+16)
  • (added) libc/test/src/math/cospif_test.cpp (+112)
  • (modified) libc/test/src/math/exhaustive/CMakeLists.txt (+16)
  • (added) libc/test/src/math/exhaustive/cospif_test.cpp (+33)
  • (modified) libc/test/src/math/smoke/CMakeLists.txt (+11)
  • (added) libc/test/src/math/smoke/cospif_test.cpp (+34)
  • (modified) libc/utils/MPFRWrapper/MPFRUtils.cpp (+19)
  • (modified) libc/utils/MPFRWrapper/MPFRUtils.h (+1)
diff --git a/libc/config/darwin/arm/entrypoints.txt b/libc/config/darwin/arm/entrypoints.txt
index cb4603c79c79c..d32745c9c9467 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 ff35e8fffec19..79e880b1bec8e 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 51d85eed9ff16..fb9e4e3763844 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 3eefa129c9758..6a61b51d60242 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/docs/math/index.rst b/libc/docs/math/index.rst
index e4da3d42baf7a..6c84b10122677 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/CMakeLists.txt b/libc/src/math/CMakeLists.txt
index 5b20913134fdf..ce4f8f993526d 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 0000000000000..50935bc33e59d
--- /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 d6ea8c54174b6..6ace14063bcb1 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 0000000000000..707f76c087074
--- /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:
+  //   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
+  // 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/src/math/generic/sinpif.cpp b/libc/src/math/generic/sinpif.cpp
index 662263c9fc43e..05bdad3ab4d0e 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
   //
diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt
index 637e6720400ff..0ae7464e8aa40 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 0000000000000..8f34dc4a2f617
--- /dev/null
+++ b/libc/test/src/math/cospif_test.cpp
@@ -0,0 +1,112 @@
+//===-- 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"
+
+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 412ca031d0e99..c5f75b51cbd9f 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 0000000000000..59077d5909937
--- /dev/null
+++ b/libc/test/src/math/exhaustive/cospif_test.cpp
@@ -0,0 +1,33 @@
+//===-- 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 "src/math/cospif.h"
+#include "utils/MPFRWrapper/MPFRUtils.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 1b269edaa2477..72d04439e2d93 100644
--- a/libc/test/src/math/smoke/CMakeLists.txt
+++ b/libc/test/src/math/smoke/CMakeLists.txt
@@ -10,6 +10,17 @@ add_fp_unittest(
   DEPENDS
     libc.src.errno.errno
     libc.src.math.cosf
+)
+
+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
 )
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 0000000000000..cd3910c64cdd7
--- /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 f0a653824bea2..9a5946059bd9a 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();...
[truncated]

@lntue lntue self-requested a review July 2, 2024 19:42
@HendrikHuebner
Copy link
Contributor Author

The Core-Math tests have passed

@lntue
Copy link
Contributor

lntue commented Jul 2, 2024

@zimmermann6

@zimmermann6
Copy link

I get a failure for rounding down:

Running exhaustive check in --rndn mode...
all ok
Running exhaustive check in --rndz mode...
all ok
Running exhaustive check in --rndu mode...
all ok
Running exhaustive check in --rndd mode...
FAIL x=0x1.7408p+12 ref=0x0p+0 y=-0x0p+0

@HendrikHuebner
Copy link
Contributor Author

HendrikHuebner commented Jul 3, 2024

hm, I'm not sure why those tests are failing. They pass when I run them locally. I'll run them again, maybe I changed something.

@HendrikHuebner
Copy link
Contributor Author

Yeah I'm not getting any failures running ./check.sh --exhaustive cospif. Are you on the latest commit @zimmermann6 ?

@zimmermann6
Copy link

I got a similar error with revision 94012aa:

Running exhaustive check in --rndd mode...
FAIL x=0x1.6c0008p+20 ref=0x0p+0 y=-0x0p+0

This is on a AMD EPYC 7282. I can send you my libllvmlibc.a by mail if you want.

@HendrikHuebner
Copy link
Contributor Author

I can test it on an AMD EPYC 7713P later. Do you know what the buildbot runs on? It keeps failing as well which I also can't reproduce

@zimmermann6
Copy link

no I don't know for the buildbot, sorry

@lntue
Copy link
Contributor

lntue commented Jul 3, 2024

@HendrikHuebner you can add the following test cases to smoke tests:

  EXPECT_FP_EQ_ALL_ROUNDING(zero, LIBC_NAMESPACE::cospif(0.5f));
  EXPECT_FP_EQ_ALL_ROUNDING(neg_zero, LIBC_NAMESPACE::cospif(-0.5f));

double sin_k, cos_k, sin_y, cosm1_y;

sincospif_eval(xd, sin_k, cos_k, sin_y, cosm1_y);

Copy link
Contributor

Choose a reason for hiding this comment

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

Adding a similar test to your sinpif should fix the signed zero issue:

  if (LIBC_UNLIKELY(sin_y == 0 && cos_k == 0))
    return FPBits::zero(xbits.sign()).get_val();

Copy link

github-actions bot commented Jul 5, 2024

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

@HendrikHuebner
Copy link
Contributor Author

Okay, I fixed the signed zero case. The buildbot passes now as well. My mpfr implementation for older mpfr version did not handle integer inputs correctly.

@lntue lntue merged commit f8834ed into llvm:main Jul 6, 2024
7 checks passed
@zimmermann6
Copy link

I still get errors for n+1/2 when n is negative. IEEE 754-2019 says that cos(n+1/2) is always +0, whether n is positive or negative, so that cos(-x) = cos(x) always holds.

Running exhaustive check in --rndn mode...
FAIL x=-0x1.7408p+12 ref=0x0p+0 y=-0x0p+0
Running exhaustive check in --rndz mode...
FAIL x=-0x1.70008p+16 ref=0x0p+0 y=-0x0p+0
Running exhaustive check in --rndu mode...
FAIL x=-0x1.7408p+12 ref=0x0p+0 y=-0x0p+0
Running exhaustive check in --rndd mode...
FAIL x=-0x1.8p+0 ref=0x0p+0 y=-0x0p+0

@HendrikHuebner
Copy link
Contributor Author

Yes, you are right. https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3096.pdf F.10.1.12.
I will patch this later today.

@zimmermann6
Copy link

all exhaustive tests are ok now, thanks!

@HendrikHuebner
Copy link
Contributor Author

Have you tested with the commit from my new PR? #98037

@zimmermann6
Copy link

yes I tested with #98037, sorry my comment was in the wrong place

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.

4 participants