diff options
author | Hendrik Hübner <117831077+HendrikHuebner@users.noreply.github.com> | 2024-07-06 15:24:05 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-07-06 09:24:05 -0400 |
commit | f8834ed24bf11d19c96c49d42e77d4408af91fd8 (patch) | |
tree | ace37cf87e2dda8f92efcc9267e73d505010804d | |
parent | de88b2cb16af4bba659d0bb2ddf10bda681ec84d (diff) | |
download | llvm-f8834ed24bf11d19c96c49d42e77d4408af91fd8.zip llvm-f8834ed24bf11d19c96c49d42e77d4408af91fd8.tar.gz llvm-f8834ed24bf11d19c96c49d42e77d4408af91fd8.tar.bz2 |
[libc][C23][math] Implement cospif function correctly rounded for all rounding modes (#97464)
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
-rw-r--r-- | libc/config/darwin/arm/entrypoints.txt | 1 | ||||
-rw-r--r-- | libc/config/linux/aarch64/entrypoints.txt | 1 | ||||
-rw-r--r-- | libc/config/linux/riscv/entrypoints.txt | 1 | ||||
-rw-r--r-- | libc/config/linux/x86_64/entrypoints.txt | 1 | ||||
-rw-r--r-- | libc/docs/math/index.rst | 2 | ||||
-rw-r--r-- | libc/src/math/CMakeLists.txt | 1 | ||||
-rw-r--r-- | libc/src/math/cospif.h | 18 | ||||
-rw-r--r-- | libc/src/math/generic/CMakeLists.txt | 17 | ||||
-rw-r--r-- | libc/src/math/generic/cospif.cpp | 96 | ||||
-rw-r--r-- | libc/src/math/generic/sinpif.cpp | 6 | ||||
-rw-r--r-- | libc/test/src/math/CMakeLists.txt | 16 | ||||
-rw-r--r-- | libc/test/src/math/cospif_test.cpp | 120 | ||||
-rw-r--r-- | libc/test/src/math/exhaustive/CMakeLists.txt | 16 | ||||
-rw-r--r-- | libc/test/src/math/exhaustive/cospif_test.cpp | 33 | ||||
-rw-r--r-- | libc/test/src/math/smoke/CMakeLists.txt | 11 | ||||
-rw-r--r-- | libc/test/src/math/smoke/cospif_test.cpp | 34 | ||||
-rw-r--r-- | libc/utils/MPFRWrapper/MPFRUtils.cpp | 35 | ||||
-rw-r--r-- | libc/utils/MPFRWrapper/MPFRUtils.h | 1 |
18 files changed, 406 insertions, 4 deletions
diff --git a/libc/config/darwin/arm/entrypoints.txt b/libc/config/darwin/arm/entrypoints.txt index feb106c..9eb7d89 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 f7e08ec..a6aeb06 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 5b0d591..2b7e3d0 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 09f04fb..271763d 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 5c4464b..422acfcd 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 0983d26..e21011f 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 0000000..50935bc --- /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 ff84a43..fc7d699 100644 --- a/libc/src/math/generic/CMakeLists.txt +++ b/libc/src/math/generic/CMakeLists.txt @@ -218,6 +218,23 @@ add_entrypoint_object( ) 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 sin.cpp diff --git a/libc/src/math/generic/cospif.cpp b/libc/src/math/generic/cospif.cpp new file mode 100644 index 0000000..7136194 --- /dev/null +++ b/libc/src/math/generic/cospif.cpp @@ -0,0 +1,96 @@ +//===-- 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); + Sign xsign = xbits.sign(); + 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); + + if (LIBC_UNLIKELY(sin_y == 0 && cos_k == 0)) { + return FPBits::zero(xsign).get_val(); + } + + 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 662263c..05bdad3 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 f919634..35ca97b 100644 --- a/libc/test/src/math/CMakeLists.txt +++ b/libc/test/src/math/CMakeLists.txt @@ -29,6 +29,22 @@ add_fp_unittest( ) 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 SUITE diff --git a/libc/test/src/math/cospif_test.cpp b/libc/test/src/math/cospif_test.cpp new file mode 100644 index 0000000..8a39957 --- /dev/null +++ b/libc/test/src/math/cospif_test.cpp @@ -0,0 +1,120 @@ +//===-- 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 = 36; + 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 + }; + + 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) { + EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::cospif(100.5f)); + EXPECT_FP_EQ(-0.0, LIBC_NAMESPACE::cospif(-100.5f)); + EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::cospif(45678.5f)); + EXPECT_FP_EQ(-0.0, LIBC_NAMESPACE::cospif(-45678.5f)); + EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::cospif(8000000.5f)); + EXPECT_FP_EQ(-0.0, LIBC_NAMESPACE::cospif(-8000000.5f)); +} diff --git a/libc/test/src/math/exhaustive/CMakeLists.txt b/libc/test/src/math/exhaustive/CMakeLists.txt index 412ca03..c5f75b5 100644 --- a/libc/test/src/math/exhaustive/CMakeLists.txt +++ b/libc/test/src/math/exhaustive/CMakeLists.txt @@ -75,6 +75,22 @@ add_fp_unittest( ) 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 NEED_MPFR 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 0000000..59077d59 --- /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 123e8ff..b72d4b3 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 0000000..007c4c4 --- /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(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); +} diff --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp index f0a6538..6548fc3 100644 --- a/libc/utils/MPFRWrapper/MPFRUtils.cpp +++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp @@ -239,6 +239,39 @@ public: 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); + return result; +#else + MPFRNumber value_frac(*this); + mpfr_frac(value_frac.value, value, MPFR_RNDN); + + if (mpfr_cmp_si(value_frac.value, 0.0) == 0) { + mpz_t integer_part; + mpz_init(integer_part); + mpfr_get_z(integer_part, value, MPFR_RNDN); + + if (mpz_tstbit(integer_part, 0)) { + mpfr_set_si(result.value, -1.0, MPFR_RNDN); // odd + } else { + mpfr_set_si(result.value, 1.0, MPFR_RNDN); // even + } + return result; + } + + 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); + + return result; +#endif + } + MPFRNumber erf() const { MPFRNumber result(*this); mpfr_erf(result.value, value, mpfr_rounding); @@ -675,6 +708,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 213dc7a..002dc91 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, |