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][math][c23] Add atanhf16 C23 math function. #132612

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

harrisonGPU
Copy link
Contributor

@harrisonGPU harrisonGPU commented Mar 23, 2025

Implementation of atanhf16 function for 16-bit inputs.

Closes: #132209

@harrisonGPU harrisonGPU requested review from overmighty and lntue March 23, 2025 15:16
@harrisonGPU harrisonGPU self-assigned this Mar 23, 2025
@llvmbot llvmbot added the libc label Mar 23, 2025
@llvmbot
Copy link
Member

llvmbot commented Mar 23, 2025

@llvm/pr-subscribers-libc

Author: Harrison Hao (harrisonGPU)

Changes

Implementation of atanhf16 function for 16-bit inputs.


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

11 Files Affected:

  • (modified) libc/config/linux/x86_64/entrypoints.txt (+1)
  • (modified) libc/docs/headers/math/index.rst (+1-1)
  • (modified) libc/include/math.yaml (+7)
  • (modified) libc/src/math/CMakeLists.txt (+1)
  • (added) libc/src/math/atanhf16.h (+21)
  • (modified) libc/src/math/generic/CMakeLists.txt (+12-3)
  • (added) libc/src/math/generic/atanhf16.cpp (+86)
  • (modified) libc/test/src/math/CMakeLists.txt (+11)
  • (added) libc/test/src/math/atanhf16_test.cpp (+39)
  • (modified) libc/test/src/math/smoke/CMakeLists.txt (+12)
  • (added) libc/test/src/math/smoke/atanhf16_test.cpp (+58)
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index ac281e8d39066..f92f6f234bfdc 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -655,6 +655,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
     libc.src.math.asinf16
     libc.src.math.acosf16
     libc.src.math.acoshf16
+    libc.src.math.atanhf16
     libc.src.math.canonicalizef16
     libc.src.math.ceilf16
     libc.src.math.copysignf16
diff --git a/libc/docs/headers/math/index.rst b/libc/docs/headers/math/index.rst
index 16903a40c03fa..82a552a6568bf 100644
--- a/libc/docs/headers/math/index.rst
+++ b/libc/docs/headers/math/index.rst
@@ -267,7 +267,7 @@ Higher Math Functions
 +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
 | atan2pi   |                  |                 |                        |                      |                        | 7.12.4.11              | F.10.1.11                  |
 +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
-| atanh     | |check|          |                 |                        |                      |                        | 7.12.5.3               | F.10.2.3                   |
+| atanh     | |check|          |                 |                        | |check|              |                        | 7.12.5.3               | F.10.2.3                   |
 +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
 | atanpi    |                  |                 |                        |                      |                        | 7.12.4.10              | F.10.1.10                  |
 +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
diff --git a/libc/include/math.yaml b/libc/include/math.yaml
index bd29eb0213c4f..627aae9de5942 100644
--- a/libc/include/math.yaml
+++ b/libc/include/math.yaml
@@ -98,6 +98,13 @@ functions:
     return_type: float
     arguments:
       - type: float
+  - name: atanhf16
+    standards:
+      - stdc
+    return_type: _Float16
+    arguments:
+      - type: _Float16
+    guard: LIBC_TYPES_HAS_FLOAT16
   - name: canonicalize
     standards:
       - stdc
diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt
index 92c80a1053c9e..72d2e16622d1a 100644
--- a/libc/src/math/CMakeLists.txt
+++ b/libc/src/math/CMakeLists.txt
@@ -64,6 +64,7 @@ add_math_entrypoint_object(atan2l)
 
 add_math_entrypoint_object(atanh)
 add_math_entrypoint_object(atanhf)
+add_math_entrypoint_object(atanhf16)
 
 add_math_entrypoint_object(canonicalize)
 add_math_entrypoint_object(canonicalizef)
diff --git a/libc/src/math/atanhf16.h b/libc/src/math/atanhf16.h
new file mode 100644
index 0000000000000..9fbb262c16514
--- /dev/null
+++ b/libc/src/math/atanhf16.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for atanhf16 ----------------------*- 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_ATANHF16_H
+#define LLVM_LIBC_SRC_MATH_ATANHF16_H
+
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/properties/types.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+float16 atanhf16(float16 x);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_MATH_ATANHF16_H
diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt
index 77c6244c5e5da..9f3abc0cb1530 100644
--- a/libc/src/math/generic/CMakeLists.txt
+++ b/libc/src/math/generic/CMakeLists.txt
@@ -3955,12 +3955,9 @@ add_entrypoint_object(
     libc.hdr.errno_macros
     libc.hdr.fenv_macros
     libc.src.__support.FPUtil.cast
-    libc.src.__support.FPUtil.except_value_utils
-    libc.src.__support.FPUtil.fenv_impl
     libc.src.__support.FPUtil.fp_bits
     libc.src.__support.FPUtil.multiply_add
     libc.src.__support.FPUtil.polyeval
-    libc.src.__support.FPUtil.sqrt
     libc.src.__support.macros.optimization
     libc.src.__support.macros.properties.types 
 )
@@ -3992,6 +3989,18 @@ add_entrypoint_object(
     libc.src.__support.macros.optimization
 )
 
+add_entrypoint_object(
+  atanhf16
+  SRCS
+    atanhf16.cpp
+  HDRS
+    ../atanhf16.h
+  DEPENDS
+    .explogxf
+    libc.src.__support.FPUtil.fp_bits
+    libc.src.__support.macros.optimization
+)
+
 add_object_library(
   inv_trigf_utils
   HDRS
diff --git a/libc/src/math/generic/atanhf16.cpp b/libc/src/math/generic/atanhf16.cpp
new file mode 100644
index 0000000000000..94d6aa149cf00
--- /dev/null
+++ b/libc/src/math/generic/atanhf16.cpp
@@ -0,0 +1,86 @@
+//===-- Implementation of atanh(x) function -------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/math/atanhf16.h"
+#include "explogxf.h"
+#include "hdr/errno_macros.h"
+#include "hdr/fenv_macros.h"
+#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/FPUtil/PolyEval.h"
+#include "src/__support/FPUtil/cast.h"
+#include "src/__support/FPUtil/multiply_add.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/optimization.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(float16, atanhf16, (float16 x)) {
+  using FPBits = typename fputil::FPBits<float16>;
+
+  FPBits xbits(x);
+  Sign sign = xbits.sign();
+  uint16_t x_abs = xbits.abs().uintval();
+
+  if (LIBC_UNLIKELY(x_abs >= 0x3c00U)) {
+    if (xbits.is_nan()) {
+      return x;
+    }
+    // |x| == 1.0
+    if (x_abs == 0x3c00U) {
+      fputil::set_errno_if_required(ERANGE);
+      fputil::raise_except_if_required(FE_DIVBYZERO);
+      return FPBits::inf(sign).get_val();
+    } else {
+      fputil::set_errno_if_required(EDOM);
+      fputil::raise_except_if_required(FE_INVALID);
+      return FPBits::quiet_nan().get_val();
+    }
+  }
+
+  // For |x| less than approximately 0.10
+  if (LIBC_UNLIKELY(x_abs <= 0x2e66U)) {
+    // The Taylor expansion of atanh(x) is:
+    //    atanh(x) = x + x^3/3 + x^5/5 + x^7/7 + x^9/9 + x^11/11
+    //             = x * [1 + x^2/3 + x^4/5 + x^6/7 + x^8/9 + x^10/11]
+    // When |x| < 0x0100U, this can be approximated by:
+    //    atanh(x) ≈ x + (1/3)*x^3
+    if (LIBC_UNLIKELY(x_abs < 0x0100U)) {
+      return static_cast<float16>(
+          LIBC_UNLIKELY(x_abs == 0) ? x : (x + 0x1.555556p-2 * x * x * x));
+    }
+
+    // For 0x0100U <= |x| <= 0x2e66U:
+    //   Let t = x^2.
+    //   Define P(t) ≈ (1/3)*t + (1/5)*t^2 + (1/7)*t^3 + (1/9)*t^4 + (1/11)*t^5.
+    // The coefficients below were derived using Sollya:
+    //   > display = hexadecimal;
+    //   > round(1/3, SG, RN);
+    //   > round(1/5, SG, RN);
+    //   > round(1/7, SG, RN);
+    //   > round(1/9, SG, RN);
+    //   > round(1/11, SG, RN);
+    // This yields:
+    //   0x1.555556p-2
+    //   0x1.99999ap-3
+    //   0x1.24924ap-3
+    //   0x1.c71c72p-4
+    //   0x1.745d18p-4f
+    // Thus, atanh(x) ≈ x * (1 + P(x^2)).
+    float xf = x;
+    float x2 = xf * xf;
+    float pe = fputil::polyeval(x2, 0.0f, 0x1.555556p-2f, 0x1.99999ap-3f,
+                                0x1.24924ap-3f, 0x1.c71c72p-4f, 0x1.745d18p-4f);
+    return static_cast<float16>(fputil::multiply_add(xf, pe, xf));
+  }
+
+  float xf = x;
+  return static_cast<float16>(0.5 * log_eval((xf + 1.0) / (xf - 1.0)));
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt
index 6ca4163f07949..371fb0e25a6cf 100644
--- a/libc/test/src/math/CMakeLists.txt
+++ b/libc/test/src/math/CMakeLists.txt
@@ -2132,6 +2132,17 @@ add_fp_unittest(
     libc.src.__support.FPUtil.fp_bits
 )
 
+add_fp_unittest(
+  atanhf16_test
+  NEED_MPFR
+  SUITE
+    libc-math-unittests
+  SRCS
+    atanhf16_test.cpp
+  DEPENDS
+    libc.src.math.atanhf16
+)
+
 add_fp_unittest(
   fmul_test
   NEED_MPFR
diff --git a/libc/test/src/math/atanhf16_test.cpp b/libc/test/src/math/atanhf16_test.cpp
new file mode 100644
index 0000000000000..ce0179a1962df
--- /dev/null
+++ b/libc/test/src/math/atanhf16_test.cpp
@@ -0,0 +1,39 @@
+//===-- Unittests for atanhf16 --------------------------------------------===//
+//
+// 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/__support/FPUtil/FPBits.h"
+#include "src/errno/libc_errno.h"
+#include "src/math/atanhf16.h"
+#include "test/UnitTest/FPMatcher.h"
+#include "test/UnitTest/Test.h"
+#include "utils/MPFRWrapper/MPFRUtils.h"
+#include <stdint.h>
+
+using LlvmLibcAtanhf16Test = LIBC_NAMESPACE::testing::FPTest<float16>;
+namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
+
+static constexpr uint16_t POS_START = 0x0000U;
+static constexpr uint16_t POS_STOP = 0x3BFFU;
+static constexpr uint16_t NEG_START = 0xBBFFU;
+static constexpr uint16_t NEG_STOP = 0x8000U;
+
+TEST_F(LlvmLibcAtanhf16Test, PositiveRange) {
+  for (uint16_t v = POS_START; v <= POS_STOP; ++v) {
+    float16 x = FPBits(v).get_val();
+    EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Atanh, x,
+                                   LIBC_NAMESPACE::atanhf16(x), 0.5);
+  }
+}
+
+TEST_F(LlvmLibcAtanhf16Test, NegativeRange) {
+  for (uint16_t v = NEG_START; v <= NEG_STOP; --v) {
+    float16 x = FPBits(v).get_val();
+    EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Atanh, x,
+                                   LIBC_NAMESPACE::atanhf16(x), 0.5);
+  }
+}
diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt
index 660b68687d63c..aae8140f93c5b 100644
--- a/libc/test/src/math/smoke/CMakeLists.txt
+++ b/libc/test/src/math/smoke/CMakeLists.txt
@@ -3934,6 +3934,18 @@ add_fp_unittest(
     libc.src.__support.FPUtil.fp_bits
 )
 
+add_fp_unittest(
+  atanhf16_test
+  SUITE
+    libc-math-smoke-tests
+  SRCS
+    atanhf16_test.cpp
+  DEPENDS
+    libc.src.errno.errno
+    libc.src.math.atanhf16
+    libc.src.__support.FPUtil.fp_bits
+)
+
 add_fp_unittest(
   asinhf_test
   SUITE
diff --git a/libc/test/src/math/smoke/atanhf16_test.cpp b/libc/test/src/math/smoke/atanhf16_test.cpp
new file mode 100644
index 0000000000000..1ac483d2ae758
--- /dev/null
+++ b/libc/test/src/math/smoke/atanhf16_test.cpp
@@ -0,0 +1,58 @@
+//===-- Unittests for atanhf16 --------------------------------------------===//
+//
+// 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/__support/FPUtil/cast.h"
+#include "src/errno/libc_errno.h"
+#include "src/math/atanhf16.h"
+#include "test/UnitTest/FPMatcher.h"
+#include "test/UnitTest/Test.h"
+
+using LlvmLibcAtanhf16Test = LIBC_NAMESPACE::testing::FPTest<float16>;
+
+TEST_F(LlvmLibcAtanhf16Test, SpecialNumbers) {
+  LIBC_NAMESPACE::libc_errno = 0;
+  EXPECT_FP_EQ_ALL_ROUNDING(aNaN, LIBC_NAMESPACE::atanhf16(aNaN));
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ_ALL_ROUNDING(zero, LIBC_NAMESPACE::atanhf16(zero));
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ_ALL_ROUNDING(
+      -0.0f,
+      LIBC_NAMESPACE::atanhf16(LIBC_NAMESPACE::fputil::cast<float16>(-0.0f)));
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ_WITH_EXCEPTION(
+      inf,
+      LIBC_NAMESPACE::atanhf16(LIBC_NAMESPACE::fputil::cast<float16>(1.0f)),
+      FE_DIVBYZERO);
+  EXPECT_MATH_ERRNO(ERANGE);
+
+  EXPECT_FP_EQ_WITH_EXCEPTION(
+      neg_inf,
+      LIBC_NAMESPACE::atanhf16(LIBC_NAMESPACE::fputil::cast<float16>(-1.0f)),
+      FE_DIVBYZERO);
+  EXPECT_MATH_ERRNO(ERANGE);
+
+  EXPECT_FP_IS_NAN_WITH_EXCEPTION(
+      LIBC_NAMESPACE::atanhf16(LIBC_NAMESPACE::fputil::cast<float16>(2.0f)),
+      FE_INVALID);
+  EXPECT_MATH_ERRNO(EDOM);
+
+  EXPECT_FP_IS_NAN_WITH_EXCEPTION(
+      LIBC_NAMESPACE::atanhf16(LIBC_NAMESPACE::fputil::cast<float16>(-2.0f)),
+      FE_INVALID);
+  EXPECT_MATH_ERRNO(EDOM);
+
+  EXPECT_FP_IS_NAN_WITH_EXCEPTION(LIBC_NAMESPACE::atanhf16(inf), FE_INVALID);
+  EXPECT_MATH_ERRNO(EDOM);
+
+  EXPECT_FP_IS_NAN_WITH_EXCEPTION(LIBC_NAMESPACE::atanhf16(neg_inf),
+                                  FE_INVALID);
+  EXPECT_MATH_ERRNO(EDOM);
+}

@harrisonGPU
Copy link
Contributor Author

Hi @jhuber6 @overmighty @lntue Could you please review this PR, if you have time. :-)


// Range for positive numbers: [0, 1)
static constexpr uint16_t POS_START = 0x0000U;
static constexpr uint16_t POS_STOP = 0x3C00;
Copy link
Contributor

Choose a reason for hiding this comment

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

I think our MPFR testing now can take care of inf/nan cases. So you should be able to test for the entire range.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay, I have updated it. :-)

Comment on lines 61 to 73
// The coefficients below were derived using Sollya:
// > display = hexadecimal;
// > round(1/3, SG, RN);
// > round(1/5, SG, RN);
// > round(1/7, SG, RN);
// > round(1/9, SG, RN);
// > round(1/11, SG, RN);
// This yields:
// 0x1.555556p-2
// 0x1.99999ap-3
// 0x1.24924ap-3
// 0x1.c71c72p-4
// 0x1.745d18p-4f
Copy link
Member

Choose a reason for hiding this comment

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

This is too verbose in my opinion.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have updated it, what do you think about it? @overmighty

}

float xf = x;
return static_cast<float16>(0.5 * log_eval((xf + 1.0) / (xf - 1.0)));
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
return static_cast<float16>(0.5 * log_eval((xf + 1.0) / (xf - 1.0)));
return fputil::cast<float16>(0.5 * log_eval((xf + 1.0) / (xf - 1.0)));

Copy link
Contributor

Choose a reason for hiding this comment

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

Also use float literals.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay, I have updated it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[libc][math][c23] Implement C23 math function atanhf16
4 participants