[llvm-branch-commits] [libc] [llvm] [libc][math] Implement double precision cosh correctly rounded to all rounding modes. (PR #235181)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Sun Oct 25 19:17:09 PDT 2026
https://github.com/lntue updated https://github.com/llvm/llvm-project/pull/235181
>From 44236bb03c3c5a42b782002a60d834da06ad5852 Mon Sep 17 00:00:00 2001
From: Tue Ly <lntue.h at gmail.com>
Date: Sat, 24 Oct 2026 02:17:10 +0000
Subject: [PATCH] [libc][math] Implement double precision cosh correctly
rounded to all rounding modes.
Use shared exp(x) +- exp(-x) evaluations with sinh from https://github.com/llvm/llvm-project/pull/235097.
Assisted-by: Gemini for analysis and refactoring.
---
libc/config/baremetal/aarch64/entrypoints.txt | 1 +
libc/config/baremetal/arm/entrypoints.txt | 1 +
libc/config/baremetal/riscv/entrypoints.txt | 1 +
libc/config/darwin/aarch64/entrypoints.txt | 1 +
libc/config/freebsd/x86_64/entrypoints.txt | 1 +
libc/config/gpu/amdgpu/entrypoints.txt | 1 +
libc/config/gpu/nvptx/entrypoints.txt | 1 +
libc/config/linux/aarch64/entrypoints.txt | 1 +
libc/config/linux/arm/entrypoints.txt | 1 +
libc/config/linux/riscv/entrypoints.txt | 1 +
libc/config/linux/x86_64/entrypoints.txt | 1 +
libc/config/windows/entrypoints.txt | 1 +
libc/docs/headers/math/index.md | 2 +-
libc/include/math.yaml | 6 +
libc/shared/math.h | 1 +
libc/shared/math/cosh.h | 28 +++
libc/src/__support/math/CMakeLists.txt | 29 +++
libc/src/__support/math/cosh.h | 34 ++++
libc/src/__support/math/cosh_double_eval.h | 94 +++++++++
libc/src/math/generic/CMakeLists.txt | 10 +
libc/src/math/generic/cosh.cpp | 21 ++
libc/test/shared/CMakeLists.txt | 1 +
libc/test/shared/shared_math_test.cpp | 1 +
libc/test/src/math/CMakeLists.txt | 16 ++
libc/test/src/math/cosh_test.cpp | 187 ++++++++++++++++++
libc/test/src/math/smoke/CMakeLists.txt | 15 ++
libc/test/src/math/smoke/cosh_test.cpp | 125 ++++++++++++
.../llvm-project-overlay/libc/BUILD.bazel | 34 ++++
.../libc/test/src/math/BUILD.bazel | 8 +
.../libc/test/src/math/smoke/BUILD.bazel | 8 +
30 files changed, 631 insertions(+), 1 deletion(-)
create mode 100644 libc/shared/math/cosh.h
create mode 100644 libc/src/__support/math/cosh.h
create mode 100644 libc/src/__support/math/cosh_double_eval.h
create mode 100644 libc/src/math/generic/cosh.cpp
create mode 100644 libc/test/src/math/cosh_test.cpp
create mode 100644 libc/test/src/math/smoke/cosh_test.cpp
diff --git a/libc/config/baremetal/aarch64/entrypoints.txt b/libc/config/baremetal/aarch64/entrypoints.txt
index db6033e9a2c04c..6ac24afcfbbcdc 100644
--- a/libc/config/baremetal/aarch64/entrypoints.txt
+++ b/libc/config/baremetal/aarch64/entrypoints.txt
@@ -385,6 +385,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.copysignl
libc.src.math.cos
libc.src.math.cosf
+ libc.src.math.cosh
libc.src.math.coshf
libc.src.math.cospif
libc.src.math.dfmal
diff --git a/libc/config/baremetal/arm/entrypoints.txt b/libc/config/baremetal/arm/entrypoints.txt
index e4964aa02a6ec5..a71e3fc8cb1c8a 100644
--- a/libc/config/baremetal/arm/entrypoints.txt
+++ b/libc/config/baremetal/arm/entrypoints.txt
@@ -397,6 +397,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.copysignl
libc.src.math.cos
libc.src.math.cosf
+ libc.src.math.cosh
libc.src.math.coshf
libc.src.math.cospif
libc.src.math.dfmal
diff --git a/libc/config/baremetal/riscv/entrypoints.txt b/libc/config/baremetal/riscv/entrypoints.txt
index d9baaaeba8a10e..1a481461473787 100644
--- a/libc/config/baremetal/riscv/entrypoints.txt
+++ b/libc/config/baremetal/riscv/entrypoints.txt
@@ -388,6 +388,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.copysignl
libc.src.math.cos
libc.src.math.cosf
+ libc.src.math.cosh
libc.src.math.coshf
libc.src.math.cospif
libc.src.math.dfmal
diff --git a/libc/config/darwin/aarch64/entrypoints.txt b/libc/config/darwin/aarch64/entrypoints.txt
index cabd03e2442dfb..20ab7d6e180528 100644
--- a/libc/config/darwin/aarch64/entrypoints.txt
+++ b/libc/config/darwin/aarch64/entrypoints.txt
@@ -206,6 +206,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.copysignl
libc.src.math.cos
libc.src.math.cosf
+ libc.src.math.cosh
libc.src.math.coshf
libc.src.math.cospif
libc.src.math.dfmal
diff --git a/libc/config/freebsd/x86_64/entrypoints.txt b/libc/config/freebsd/x86_64/entrypoints.txt
index 6d567b01571e84..4c256333d9228b 100644
--- a/libc/config/freebsd/x86_64/entrypoints.txt
+++ b/libc/config/freebsd/x86_64/entrypoints.txt
@@ -140,6 +140,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.ceill
libc.src.math.cos
libc.src.math.cosf
+ libc.src.math.cosh
libc.src.math.coshf
libc.src.math.daddl
libc.src.math.ddivl
diff --git a/libc/config/gpu/amdgpu/entrypoints.txt b/libc/config/gpu/amdgpu/entrypoints.txt
index 196b6a3fd20266..880327cba1cbb0 100644
--- a/libc/config/gpu/amdgpu/entrypoints.txt
+++ b/libc/config/gpu/amdgpu/entrypoints.txt
@@ -331,6 +331,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.copysignl
libc.src.math.cos
libc.src.math.cosf
+ libc.src.math.cosh
libc.src.math.coshf
libc.src.math.cospif
libc.src.math.ddivl
diff --git a/libc/config/gpu/nvptx/entrypoints.txt b/libc/config/gpu/nvptx/entrypoints.txt
index 96305e3d1293f1..1c8a02af59a836 100644
--- a/libc/config/gpu/nvptx/entrypoints.txt
+++ b/libc/config/gpu/nvptx/entrypoints.txt
@@ -327,6 +327,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.copysignl
libc.src.math.cos
libc.src.math.cosf
+ libc.src.math.cosh
libc.src.math.coshf
libc.src.math.cospif
libc.src.math.ddivl
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index f78eb4d3c8a372..79626a1ad727d7 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -627,6 +627,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.copysignl
libc.src.math.cos
libc.src.math.cosf
+ libc.src.math.cosh
libc.src.math.coshf
libc.src.math.cospif
libc.src.math.daddl
diff --git a/libc/config/linux/arm/entrypoints.txt b/libc/config/linux/arm/entrypoints.txt
index 3dd2eede1952d4..2d8bbf275517e9 100644
--- a/libc/config/linux/arm/entrypoints.txt
+++ b/libc/config/linux/arm/entrypoints.txt
@@ -423,6 +423,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.copysignl
libc.src.math.cos
libc.src.math.cosf
+ libc.src.math.cosh
libc.src.math.coshf
libc.src.math.dfmal
libc.src.math.dsqrtl
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 689eeb8f6e3d69..a5f834f8e2c62d 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -684,6 +684,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.copysignl
libc.src.math.cos
libc.src.math.cosf
+ libc.src.math.cosh
libc.src.math.coshf
libc.src.math.cospif
libc.src.math.daddl
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index a42172e2cce6fa..110dca3a73ab72 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -706,6 +706,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.copysignl
libc.src.math.cos
libc.src.math.cosf
+ libc.src.math.cosh
libc.src.math.coshf
libc.src.math.cospif
libc.src.math.daddl
diff --git a/libc/config/windows/entrypoints.txt b/libc/config/windows/entrypoints.txt
index f38000fcddb7ac..4abd600c61d1dc 100644
--- a/libc/config/windows/entrypoints.txt
+++ b/libc/config/windows/entrypoints.txt
@@ -172,6 +172,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.ceill
libc.src.math.cos
libc.src.math.cosf
+ libc.src.math.cosh
libc.src.math.coshf
libc.src.math.daddl
libc.src.math.ddivl
diff --git a/libc/docs/headers/math/index.md b/libc/docs/headers/math/index.md
index a4596408ee5648..e0393075cd900b 100644
--- a/libc/docs/headers/math/index.md
+++ b/libc/docs/headers/math/index.md
@@ -180,7 +180,7 @@ log
| cbrt | {{ check }} | {{ check }} | | {{ check }} | | {{ check }} | 7.12.7.1 | F.10.4.1 |
| compoundn | | | | | | | 7.12.7.2 | F.10.4.2 |
| cos | {{ check }} | {{ check }} | | {{ check }} | | | 7.12.4.5 | F.10.1.5 |
-| cosh | {{ check }} | | | {{ check }} | | | 7.12.5.4 | F.10.2.4 |
+| cosh | {{ check }} | {{ check }} | | {{ check }} | | | 7.12.5.4 | F.10.2.4 |
| cospi | {{ check }} | | | {{ check }} | | | 7.12.4.12 | F.10.1.12 |
| dsqrt | N/A | N/A | {{ check }} | N/A | {{ check }}\* | | 7.12.14.6 | F.10.11 |
| erf | {{ check }} | | | | | | 7.12.8.1 | F.10.5.1 |
diff --git a/libc/include/math.yaml b/libc/include/math.yaml
index 89e0ab4f0dac60..09668300f9ea88 100644
--- a/libc/include/math.yaml
+++ b/libc/include/math.yaml
@@ -323,6 +323,12 @@ functions:
arguments:
- type: _Float16
guard: LIBC_TYPES_HAS_FLOAT16
+ - name: cosh
+ standards:
+ - stdc
+ return_type: double
+ arguments:
+ - type: double
- name: coshf
standards:
- stdc
diff --git a/libc/shared/math.h b/libc/shared/math.h
index 04f85e000bd0b6..1f676b8da2e4f8 100644
--- a/libc/shared/math.h
+++ b/libc/shared/math.h
@@ -84,6 +84,7 @@
#include "math/cos.h"
#include "math/cosf.h"
#include "math/cosf16.h"
+#include "math/cosh.h"
#include "math/coshf.h"
#include "math/coshf16.h"
#include "math/cospif.h"
diff --git a/libc/shared/math/cosh.h b/libc/shared/math/cosh.h
new file mode 100644
index 00000000000000..7e21496ebcd9b4
--- /dev/null
+++ b/libc/shared/math/cosh.h
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Shared cosh function.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SHARED_MATH_COSH_H
+#define LLVM_LIBC_SHARED_MATH_COSH_H
+
+#include "shared/libc_common.h"
+#include "src/__support/math/cosh.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace shared {
+
+using math::cosh;
+
+} // namespace shared
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SHARED_MATH_COSH_H
diff --git a/libc/src/__support/math/CMakeLists.txt b/libc/src/__support/math/CMakeLists.txt
index 29b085e1d0c163..390220bd5a4b88 100644
--- a/libc/src/__support/math/CMakeLists.txt
+++ b/libc/src/__support/math/CMakeLists.txt
@@ -5772,6 +5772,35 @@ add_header_library(
libc.src.__support.macros.properties.cpu_features
)
+add_header_library(
+ cosh_double_eval
+ HDRS
+ cosh_double_eval.h
+ DEPENDS
+ .sinhcosh_utils
+ libc.hdr.errno_macros
+ libc.hdr.fenv_macros
+ libc.src.__support.FPUtil.except_value_utils
+ libc.src.__support.FPUtil.fenv_impl
+ libc.src.__support.FPUtil.fp_bits
+ libc.src.__support.FPUtil.rounding_mode
+ libc.src.__support.common
+ libc.src.__support.macros.attributes
+ libc.src.__support.macros.config
+ libc.src.__support.macros.optimization
+)
+
+add_header_library(
+ cosh
+ HDRS
+ cosh.h
+ DEPENDS
+ .cosh_double_eval
+ libc.src.__support.common
+ libc.src.__support.macros.config
+ libc.src.__support.macros.optimization
+)
+
add_header_library(
sinh_double_eval
HDRS
diff --git a/libc/src/__support/math/cosh.h b/libc/src/__support/math/cosh.h
new file mode 100644
index 00000000000000..07248d0b0023b3
--- /dev/null
+++ b/libc/src/__support/math/cosh.h
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation header for cosh.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_MATH_COSH_H
+#define LLVM_LIBC_SRC___SUPPORT_MATH_COSH_H
+
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/optimization.h"
+
+#include "src/__support/math/cosh_double_eval.h"
+#define LIBC_MATH_COSH_IMPL double_eval
+
+namespace LIBC_NAMESPACE_DECL {
+namespace math {
+
+using LIBC_MATH_COSH_IMPL::cosh;
+
+} // namespace math
+} // namespace LIBC_NAMESPACE_DECL
+
+#undef LIBC_MATH_COSH_IMPL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_MATH_COSH_H
diff --git a/libc/src/__support/math/cosh_double_eval.h b/libc/src/__support/math/cosh_double_eval.h
new file mode 100644
index 00000000000000..0511cee00cdaa3
--- /dev/null
+++ b/libc/src/__support/math/cosh_double_eval.h
@@ -0,0 +1,94 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Double-precision implementation of cosh.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_MATH_COSH_DOUBLE_EVAL_H
+#define LLVM_LIBC_SRC___SUPPORT_MATH_COSH_DOUBLE_EVAL_H
+
+#include "hdr/errno_macros.h"
+#include "hdr/fenv_macros.h"
+#include "src/__support/FPUtil/FEnvImpl.h"
+#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/FPUtil/except_value_utils.h"
+#include "src/__support/FPUtil/rounding_mode.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/attributes.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/optimization.h"
+#include "src/__support/math/sinhcosh_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace math {
+namespace double_eval {
+
+// Handle tiny (|x| <= 2^-27), overflow (|x| >= 0x1.633ce8fb9f87ep+9), inf, and
+// nan for cosh(x):
+LIBC_NOINLINE LIBC_INLINE double cosh_exceptional(double x) {
+ using FPBits = fputil::FPBits<double>;
+ FPBits xbits(x);
+
+ // Tiny and subnormal range: |x| <= 2^-27.
+ // For 0 < |x| <= 2^-27:
+ // 0 < cosh(x) - 1 ~ x^2 / 2 < 2^-55 < 0.5 * ulp(1.0),
+ // so cosh(x) rounds to 1.0 + 2^-52 when rounding mode is FE_UPWARD and to
+ // 1.0 otherwise, with FE_INEXACT raised for all x != 0.
+ if (xbits.abs().uintval() <= 0x3e40'0000'0000'0000ULL) {
+ if (LIBC_UNLIKELY(x == 0.0))
+ return 1.0;
+ return fputil::round_result_slightly_up(1.0);
+ }
+
+ if (xbits.is_nan())
+ return x + 1.0;
+
+ if (xbits.is_inf())
+ return FPBits::inf().get_val();
+
+ fputil::set_errno_if_required(ERANGE);
+ fputil::raise_overflow_except_if_required<double>();
+
+#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+ int rounding = fputil::quick_get_round();
+ if (LIBC_UNLIKELY(rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO))
+ return FPBits::max_normal().get_val();
+#endif // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+
+ return FPBits::inf().get_val();
+}
+
+LIBC_ALWAYS_INLINE double cosh(double x) {
+ using FPBits = fputil::FPBits<double>;
+ FPBits xbits(x);
+
+ uint64_t x_abs = xbits.abs().uintval();
+
+ // Bound constants (with biased exponent and mantissa bits):
+ // - Tiny bound: 2^-27 = 0x1.0p-27
+ constexpr uint64_t TINY_BOUND = 0x3e40'0000'0000'0000ULL;
+ // - Overflow bound:
+ // > round(acosh(2^1023 * (2 - 2^-52)), D, RU) = 0x1.633ce8fb9f87ep+9
+ // > round(acosh(2^1023 * (2 - 2^-52)), D, RD) = 0x1.633ce8fb9f87dp+9
+ // > round(cosh(0x1.633ce8fb9f87dp+9), D, RN) = 0x1.ffffffffffd3bp+1023
+ // > round(cosh(0x1.633ce8fb9f87ep+9), D, RN) = +inf
+ constexpr uint64_t OVERFLOW_BOUND = 0x4086'33ce'8fb9'f87eULL;
+
+ if (LIBC_UNLIKELY(x_abs <= TINY_BOUND || x_abs >= OVERFLOW_BOUND))
+ return cosh_exceptional(x);
+
+ return exp_pm_eval(xbits.abs().get_val(), /*is_sinh=*/false);
+}
+
+} // namespace double_eval
+} // namespace math
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_MATH_COSH_DOUBLE_EVAL_H
diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt
index 6b025c4fa60620..9f0325832248ec 100644
--- a/libc/src/math/generic/CMakeLists.txt
+++ b/libc/src/math/generic/CMakeLists.txt
@@ -3733,6 +3733,16 @@ add_entrypoint_object(
libc.src.__support.math.ufromfpxbf16
)
+add_entrypoint_object(
+ cosh
+ SRCS
+ cosh.cpp
+ HDRS
+ ../cosh.h
+ DEPENDS
+ libc.src.__support.math.cosh
+)
+
add_entrypoint_object(
coshf
SRCS
diff --git a/libc/src/math/generic/cosh.cpp b/libc/src/math/generic/cosh.cpp
new file mode 100644
index 00000000000000..3d34c90f1a54ce
--- /dev/null
+++ b/libc/src/math/generic/cosh.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Double-precision cosh(x) function.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/math/cosh.h"
+#include "src/__support/math/cosh.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(double, cosh, (double x)) { return math::cosh(x); }
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/test/shared/CMakeLists.txt b/libc/test/shared/CMakeLists.txt
index 80c3eb1e1a8f20..f902fa553e49ef 100644
--- a/libc/test/shared/CMakeLists.txt
+++ b/libc/test/shared/CMakeLists.txt
@@ -93,6 +93,7 @@ else()
libc.src.__support.math.cos
libc.src.__support.math.cosf
libc.src.__support.math.cosf16
+ libc.src.__support.math.cosh
libc.src.__support.math.coshf
libc.src.__support.math.coshf16
libc.src.__support.math.cospif
diff --git a/libc/test/shared/shared_math_test.cpp b/libc/test/shared/shared_math_test.cpp
index 1d70b4d3578203..73a5cb45ed2b9c 100644
--- a/libc/test/shared/shared_math_test.cpp
+++ b/libc/test/shared/shared_math_test.cpp
@@ -337,6 +337,7 @@ TEST(LlvmLibcSharedMathTest, AllDouble) {
EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::shared::atan2(0.0, 0.0));
EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::shared::cbrt(0.0));
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::shared::cos(0.0));
+ EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::shared::cosh(0.0));
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::shared::exp(0.0));
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::shared::exp2(0.0));
EXPECT_FP_EQ(1.0, LIBC_NAMESPACE::shared::exp10(0.0));
diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt
index 394f347782807b..9ec8422b57f081 100644
--- a/libc/test/src/math/CMakeLists.txt
+++ b/libc/test/src/math/CMakeLists.txt
@@ -2568,6 +2568,22 @@ add_fp_unittest(
libc.src.__support.FPUtil.fp_bits
)
+add_fp_unittest(
+ cosh_test
+ NEED_MPFR
+ SUITE
+ libc-math-tests
+ SRCS
+ cosh_test.cpp
+ DEPENDS
+ libc.hdr.fenv_macros
+ libc.src.math.cosh
+ libc.src.__support.math.cosh_double_eval
+ libc.src.__support.FPUtil.fp_bits
+ libc.src.__support.macros.attributes
+ libc.src.__support.macros.optimization
+)
+
add_fp_unittest(
coshf_test
NEED_MPFR
diff --git a/libc/test/src/math/cosh_test.cpp b/libc/test/src/math/cosh_test.cpp
new file mode 100644
index 00000000000000..150528bb0e572d
--- /dev/null
+++ b/libc/test/src/math/cosh_test.cpp
@@ -0,0 +1,187 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains unittests for cosh.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/fenv_macros.h"
+#include "hdr/math_macros.h"
+#include "hdr/stdint_proxy.h"
+#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/macros/attributes.h"
+#include "src/__support/macros/optimization.h"
+#include "src/__support/math/cosh_double_eval.h"
+#include "src/math/cosh.h"
+#include "test/UnitTest/FPMatcher.h"
+#include "test/UnitTest/Test.h"
+#include "utils/MPFRWrapper/MPFRUtils.h"
+
+#ifdef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
+#define TOLERANCE 1
+#else // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS
+#define TOLERANCE 0
+#endif // LIBC_MATH_HAS_SKIP_ACCURATE_PASS
+
+#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+#define ALL_ROUNDING false
+#else
+#define ALL_ROUNDING true
+#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+
+namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
+using LIBC_NAMESPACE::testing::tlog;
+
+LIBC_NOINLINE double cosh_double_eval(double x) {
+ return LIBC_NAMESPACE::math::double_eval::cosh(x);
+}
+
+class CoshTest : public LIBC_NAMESPACE::testing::FPTest<double> {
+public:
+ void test_special_numbers(double (*func)(double)) {
+ EXPECT_FP_EQ(aNaN, func(aNaN));
+ EXPECT_FP_EQ(inf, func(inf));
+ EXPECT_FP_EQ_ALL_ROUNDING(inf, func(neg_inf));
+ EXPECT_FP_EQ_ALL_ROUNDING(1.0, func(0.0));
+ EXPECT_FP_EQ_ALL_ROUNDING(1.0, func(-0.0));
+ }
+
+ void test_overflow(double (*func)(double)) {
+ EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(0x1.0p20), FE_OVERFLOW);
+ EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(-0x1.0p20), FE_OVERFLOW);
+ }
+
+ void test_tricky_inputs(double (*func)(double), double ulp_tolerance = 0.5,
+ bool all_rounding = true) {
+ constexpr int N = 20;
+ constexpr uint64_t INPUTS[N] = {
+ 0x3e40'0000'0000'0000ULL, // x = 0x1.0p-27
+ 0x3e40'0000'0000'0001ULL, // x = 0x1.0000000000001p-27
+ 0x3eb0'1fe0'3f61'baceULL, // x = 0x1.01fe03f61bacep-20
+ 0x3f10'1218'4c8c'9313ULL, // x = 0x1.012184c8c9313p-14
+ 0x3f80'1813'9e73'ee47ULL, // x = 0x1.018139e73ee47p-7
+ 0x3fa0'10b2'a872'e30bULL, // x = 0x1.010b2a872e30bp-5
+ 0x3fb9'a5e3'cbe1'985eULL, // x = 0x1.9a5e3cbe1985ep-4
+ 0x3fc5'2a11'832e'847dULL, // x = 0x1.52a11832e847dp-3
+ 0x3fcb'f030'5e2c'6c37ULL, // x = 0x1.bf0305e2c6c37p-3
+ 0x3fd1'7326'ffc0'9f68ULL, // x = 0x1.17326ffc09f68p-2
+ 0x3fd3'd27b'f16d'8bdbULL, // x = 0x1.3d27bf16d8bdbp-2
+ 0x3fe0'3923'f2b4'7c07ULL, // x = 0x1.03923f2b47c07p-1
+ 0x3fea'6031'cd5f'93baULL, // x = 0x1.a6031cd5f93bap-1
+ 0x3ff1'04b6'48f1'13a1ULL, // x = 0x1.104b648f113a1p+0
+ 0x3ff1'5857'20f3'5cd9ULL, // x = 0x1.1585720f35cd9p+0
+ 0x3ffe'9cc7'ed2e'1a7eULL, // x = 0x1.e9cc7ed2e1a7ep+0
+ 0x4004'3180'ea85'4696ULL, // x = 0x1.43180ea854696p+1
+ 0x4017'2581'1dcf'6782ULL, // x = 0x1.725811dcf6782p+2
+ 0x4086'33ce'8fb9'f87dULL, // x = 0x1.633ce8fb9f87dp+9
+ 0x4086'33ce'8fb9'f87eULL, // x = 0x1.633ce8fb9f87ep+9
+ };
+ for (int i = 0; i < N; ++i) {
+ double x = FPBits(INPUTS[i]).get_val();
+ if (all_rounding) {
+ EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Cosh, x, func(x),
+ ulp_tolerance);
+ EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Cosh, -x, func(-x),
+ ulp_tolerance);
+ } else {
+ EXPECT_MPFR_MATCH(mpfr::Operation::Cosh, x, func(x), ulp_tolerance);
+ EXPECT_MPFR_MATCH(mpfr::Operation::Cosh, -x, func(-x), ulp_tolerance);
+ }
+ }
+ }
+
+ void test_in_range(double (*func)(double), double ulp_tolerance = 0.5,
+ bool all_rounding = true) {
+ constexpr uint64_t COUNT = 1'231;
+ constexpr uint64_t START = FPBits(0x1.0p-30).uintval();
+ constexpr uint64_t STOP = FPBits(710.0).uintval();
+ constexpr uint64_t STEP = (STOP - START) / COUNT;
+
+ auto test = [&](mpfr::RoundingMode rounding_mode) {
+ mpfr::ForceRoundingMode __r(rounding_mode);
+ if (!__r.success)
+ return;
+
+ uint64_t fails = 0;
+ uint64_t count = 0;
+ uint64_t cc = 0;
+ double mx = 0.0, mr = 0.0;
+ double tol = 0.5;
+
+ for (uint64_t i = 0, v = START; i <= COUNT; ++i, v += STEP) {
+ double x = FPBits(v).get_val();
+ if (FPBits(v).is_nan() || FPBits(v).is_inf() || x < 0.0)
+ continue;
+ double result = func(x);
+ ++cc;
+ if (FPBits(result).is_nan() || FPBits(result).is_inf())
+ continue;
+
+ ++count;
+ if (!TEST_MPFR_MATCH_ROUNDING_SILENTLY(mpfr::Operation::Cosh, x, result,
+ ulp_tolerance, rounding_mode)) {
+ ++fails;
+ while (!TEST_MPFR_MATCH_ROUNDING_SILENTLY(
+ mpfr::Operation::Cosh, x, result, tol, rounding_mode)) {
+ mx = x;
+ mr = result;
+
+ if (tol > 1000.0)
+ break;
+
+ tol *= 2.0;
+ }
+ }
+ }
+ if (fails) {
+ tlog << " Cosh failed: " << fails << "/" << count << "/" << cc
+ << " tests.\n";
+ tlog << " Max ULPs is at most: " << static_cast<uint64_t>(tol)
+ << ".\n";
+ EXPECT_MPFR_MATCH(mpfr::Operation::Cosh, mx, mr, ulp_tolerance,
+ rounding_mode);
+ }
+ };
+
+ tlog << " Test Rounding To Nearest...\n";
+ test(mpfr::RoundingMode::Nearest);
+
+ if (all_rounding) {
+ tlog << " Test Rounding Downward...\n";
+ test(mpfr::RoundingMode::Downward);
+
+ tlog << " Test Rounding Upward...\n";
+ test(mpfr::RoundingMode::Upward);
+
+ tlog << " Test Rounding Toward Zero...\n";
+ test(mpfr::RoundingMode::TowardZero);
+ }
+ }
+};
+
+#define LIST_COSH_TESTS(suffix, func, ulp_tolerance, all_rounding) \
+ using LlvmLibcCoshTest##suffix = CoshTest; \
+ TEST_F(LlvmLibcCoshTest##suffix, SpecialNumbers) { \
+ test_special_numbers(&func); \
+ } \
+ TEST_F(LlvmLibcCoshTest##suffix, Overflow) { test_overflow(&func); } \
+ TEST_F(LlvmLibcCoshTest##suffix, TrickyInputs) { \
+ test_tricky_inputs(&func, ulp_tolerance, all_rounding); \
+ } \
+ TEST_F(LlvmLibcCoshTest##suffix, InDoubleRange) { \
+ test_in_range(&func, ulp_tolerance, all_rounding); \
+ } \
+ static_assert(true, "Require semicolon.")
+
+LIST_COSH_TESTS(Default, LIBC_NAMESPACE::cosh,
+ /*ulp_tolerance=*/TOLERANCE + 0.5,
+ /*all_rounding=*/ALL_ROUNDING);
+LIST_COSH_TESTS(DoubleEval, cosh_double_eval,
+ /*ulp_tolerance=*/TOLERANCE + 0.5,
+ /*all_rounding=*/ALL_ROUNDING);
diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt
index 448add6cf8a73f..7485f5409402f6 100644
--- a/libc/test/src/math/smoke/CMakeLists.txt
+++ b/libc/test/src/math/smoke/CMakeLists.txt
@@ -4732,6 +4732,21 @@ add_fp_unittest(
libc.src.__support.FPUtil.fenv_impl
)
+add_fp_unittest(
+ cosh_test
+ SUITE
+ libc-math-smoke-tests
+ SRCS
+ cosh_test.cpp
+ DEPENDS
+ libc.hdr.errno_macros
+ libc.hdr.fenv_macros
+ libc.src.math.cosh
+ libc.src.__support.math.cosh_double_eval
+ libc.src.__support.FPUtil.fp_bits
+ libc.src.__support.macros.attributes
+)
+
add_fp_unittest(
coshf_test
SUITE
diff --git a/libc/test/src/math/smoke/cosh_test.cpp b/libc/test/src/math/smoke/cosh_test.cpp
new file mode 100644
index 00000000000000..757a6d2feb3c16
--- /dev/null
+++ b/libc/test/src/math/smoke/cosh_test.cpp
@@ -0,0 +1,125 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains smoke tests for cosh.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/errno_macros.h"
+#include "hdr/fenv_macros.h"
+#include "hdr/math_macros.h"
+#include "hdr/stdint_proxy.h"
+#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/macros/attributes.h"
+#include "src/__support/math/cosh_double_eval.h"
+#include "src/math/cosh.h"
+#include "test/UnitTest/FPMatcher.h"
+#include "test/UnitTest/Test.h"
+
+LIBC_NOINLINE double cosh_double_eval(double x) {
+ return LIBC_NAMESPACE::math::double_eval::cosh(x);
+}
+
+class CoshTest : public LIBC_NAMESPACE::testing::FPTest<double> {
+public:
+ void test_special_numbers(double (*func)(double),
+ bool check_snan_invalid = false) {
+ if (check_snan_invalid) {
+ EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, func(sNaN), FE_INVALID);
+ } else {
+ EXPECT_FP_EQ(aNaN, func(sNaN));
+ }
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ_ALL_ROUNDING(aNaN, func(aNaN));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ_ALL_ROUNDING(inf, func(inf));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ_ALL_ROUNDING(inf, func(neg_inf));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ_ALL_ROUNDING(1.0, func(0.0));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ_ALL_ROUNDING(1.0, func(-0.0));
+ EXPECT_MATH_ERRNO(0);
+ }
+
+ void test_overflow(double (*func)(double)) {
+ // x = 0x1.fffffffffffffp+1023 (max_normal)
+ EXPECT_FP_EQ_WITH_EXCEPTION(
+ inf, func(FPBits(uint64_t(0x7fef'ffff'ffff'ffff)).get_val()),
+ FE_OVERFLOW);
+ EXPECT_MATH_ERRNO(ERANGE);
+
+ // x = 0x1.633ce8fb9f87ep+9 (smallest positive input that overflows)
+ EXPECT_FP_EQ_WITH_EXCEPTION(
+ inf, func(FPBits(uint64_t(0x4086'33ce'8fb9'f87e)).get_val()),
+ FE_OVERFLOW);
+ EXPECT_MATH_ERRNO(ERANGE);
+
+ // x = -0x1.633ce8fb9f87ep+9 (largest negative input that overflows)
+ EXPECT_FP_EQ_WITH_EXCEPTION(
+ inf, func(FPBits(uint64_t(0xc086'33ce'8fb9'f87e)).get_val()),
+ FE_OVERFLOW);
+ EXPECT_MATH_ERRNO(ERANGE);
+ }
+
+ void test_tiny(double (*func)(double)) {
+ EXPECT_FP_EQ(1.0, func(min_denormal));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ(1.0, func(-min_denormal));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ(1.0, func(0x1.0p-27));
+ EXPECT_MATH_ERRNO(0);
+ }
+
+#ifdef LIBC_TEST_FTZ_DAZ
+ void test_denormals(double (*func)(double)) {
+ EXPECT_FP_EQ(1.0, func(min_denormal));
+ EXPECT_FP_EQ(1.0, func(max_denormal));
+ }
+#endif // LIBC_TEST_FTZ_DAZ
+};
+
+#ifdef LIBC_TEST_FTZ_DAZ
+#define LIST_COSH_FTZ_DAZ_TESTS(suffix, func) \
+ TEST_F(LlvmLibcCoshTest##suffix, FTZMode) { \
+ LIBC_NAMESPACE::testing::ModifyMXCSR mxcsr(LIBC_NAMESPACE::testing::FTZ); \
+ test_denormals(&func); \
+ } \
+ TEST_F(LlvmLibcCoshTest##suffix, DAZMode) { \
+ LIBC_NAMESPACE::testing::ModifyMXCSR mxcsr(LIBC_NAMESPACE::testing::DAZ); \
+ test_denormals(&func); \
+ } \
+ TEST_F(LlvmLibcCoshTest##suffix, FTZDAZMode) { \
+ LIBC_NAMESPACE::testing::ModifyMXCSR mxcsr(LIBC_NAMESPACE::testing::FTZ | \
+ LIBC_NAMESPACE::testing::DAZ); \
+ test_denormals(&func); \
+ }
+#else // !LIBC_TEST_FTZ_DAZ
+#define LIST_COSH_FTZ_DAZ_TESTS(suffix, func)
+#endif // LIBC_TEST_FTZ_DAZ
+
+#define LIST_COSH_TESTS(suffix, func, check_snan_invalid) \
+ using LlvmLibcCoshTest##suffix = CoshTest; \
+ TEST_F(LlvmLibcCoshTest##suffix, SpecialNumbers) { \
+ test_special_numbers(&func, check_snan_invalid); \
+ } \
+ TEST_F(LlvmLibcCoshTest##suffix, Overflow) { test_overflow(&func); } \
+ TEST_F(LlvmLibcCoshTest##suffix, Tiny) { test_tiny(&func); } \
+ LIST_COSH_FTZ_DAZ_TESTS(suffix, func) \
+ static_assert(true, "Require semicolon.")
+
+LIST_COSH_TESTS(Default, LIBC_NAMESPACE::cosh, /*check_snan_invalid=*/true);
+LIST_COSH_TESTS(DoubleEval, cosh_double_eval, /*check_snan_invalid=*/false);
diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
index 299993bbebba70..83cd98d6422cbc 100644
--- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
@@ -8702,6 +8702,35 @@ libc_support_library(
],
)
+libc_support_library(
+ name = "__support_math_cosh_double_eval",
+ hdrs = ["src/__support/math/cosh_double_eval.h"],
+ deps = [
+ ":__support_common",
+ ":__support_fputil_except_value_utils",
+ ":__support_fputil_fenv_impl",
+ ":__support_fputil_fp_bits",
+ ":__support_fputil_rounding_mode",
+ ":__support_macros_attributes",
+ ":__support_macros_config",
+ ":__support_macros_optimization",
+ ":__support_math_sinhcosh_utils",
+ ":hdr_errno_macros",
+ ":hdr_fenv_macros",
+ ],
+)
+
+libc_support_library(
+ name = "__support_math_cosh",
+ hdrs = ["src/__support/math/cosh.h"],
+ deps = [
+ ":__support_common",
+ ":__support_macros_config",
+ ":__support_macros_optimization",
+ ":__support_math_cosh_double_eval",
+ ],
+)
+
libc_support_library(
name = "__support_math_coshf",
hdrs = ["src/__support/math/coshf.h"],
@@ -13040,6 +13069,11 @@ libc_math_function(
],
)
+libc_math_function(
+ name = "cosh",
+ additional_deps = [":__support_math_cosh"],
+)
+
libc_math_function(
name = "coshf",
additional_deps = [":__support_math_coshf"],
diff --git a/utils/bazel/llvm-project-overlay/libc/test/src/math/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/test/src/math/BUILD.bazel
index 89289849d4ba73..46cf02bf9e7271 100644
--- a/utils/bazel/llvm-project-overlay/libc/test/src/math/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/test/src/math/BUILD.bazel
@@ -129,6 +129,14 @@ math_mpfr_test(
],
)
+math_mpfr_test(
+ name = "cosh",
+ deps = [
+ "//libc:__support_macros_attributes",
+ "//libc:__support_math_cosh_double_eval",
+ ],
+)
+
math_mpfr_test(
name = "coshf",
deps = [
diff --git a/utils/bazel/llvm-project-overlay/libc/test/src/math/smoke/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/test/src/math/smoke/BUILD.bazel
index e5bd15412d0611..b47989fcb6b5fb 100644
--- a/utils/bazel/llvm-project-overlay/libc/test/src/math/smoke/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/test/src/math/smoke/BUILD.bazel
@@ -250,6 +250,14 @@ math_test(name = "cos")
math_test(name = "cosf")
+math_test(
+ name = "cosh",
+ deps = [
+ "//libc:__support_macros_attributes",
+ "//libc:__support_math_cosh_double_eval",
+ ],
+)
+
math_test(
name = "coshf",
deps = [
More information about the llvm-branch-commits
mailing list