Skip to content

[libc][math][c23] Add acospif16() function #134664

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

Merged
merged 19 commits into from
Apr 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions libc/config/linux/x86_64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
# math.h C23 _Float16 entrypoints
libc.src.math.acosf16
libc.src.math.acoshf16
libc.src.math.acospif16
libc.src.math.asinf16
libc.src.math.asinhf16
libc.src.math.canonicalizef16
Expand Down
2 changes: 1 addition & 1 deletion libc/docs/headers/math/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ Higher Math Functions
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
| acosh | |check| | | | |check| | | 7.12.5.1 | F.10.2.1 |
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
| acospi | | | | | | 7.12.4.8 | F.10.1.8 |
| acospi | | | | |check| | | 7.12.4.8 | F.10.1.8 |
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
| asin | |check| | | | |check| | | 7.12.4.2 | F.10.1.2 |
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
Expand Down
7 changes: 7 additions & 0 deletions libc/include/math.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ functions:
arguments:
- type: _Float16
guard: LIBC_TYPES_HAS_FLOAT16
name: acospif16
standards:
- stdc
return_type: _Float16
arguments:
- type: _Float16
guard: LIBC_TYPES_HAS_FLOAT16
- name: asin
standards:
- stdc
Expand Down
2 changes: 2 additions & 0 deletions libc/src/math/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ add_math_entrypoint_object(acosh)
add_math_entrypoint_object(acoshf)
add_math_entrypoint_object(acoshf16)

add_math_entrypoint_object(acospif16)

add_math_entrypoint_object(asin)
add_math_entrypoint_object(asinf)
add_math_entrypoint_object(asinf16)
Expand Down
21 changes: 21 additions & 0 deletions libc/src/math/acospif16.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//===-- Implementation header for acospif16 ---------------------*- 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_ACOSPIF16_H
#define LLVM_LIBC_SRC_MATH_ACOSPIF16_H

#include "src/__support/macros/config.h"
#include "src/__support/macros/properties/types.h"

namespace LIBC_NAMESPACE_DECL {

float16 acospif16(float16 x);

} // namespace LIBC_NAMESPACE_DECL

#endif // LLVM_LIBC_SRC_MATH_ACOSPIF16_H
19 changes: 19 additions & 0 deletions libc/src/math/generic/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4112,6 +4112,25 @@ add_entrypoint_object(
libc.src.__support.macros.properties.types
)

add_entrypoint_object(
acospif16
SRCS
acospif16.cpp
HDRS
../acospif16.h
DEPENDS
libc.hdr.errno_macros
libc.hdr.fenv_macros
libc.src.__support.FPUtil.cast
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
)

add_header_library(
atan_utils
HDRS
Expand Down
134 changes: 134 additions & 0 deletions libc/src/math/generic/acospif16.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
//===-- Half-precision acospi 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/acospif16.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/PolyEval.h"
#include "src/__support/FPUtil/cast.h"
#include "src/__support/FPUtil/multiply_add.h"
#include "src/__support/FPUtil/sqrt.h"
#include "src/__support/macros/optimization.h"

namespace LIBC_NAMESPACE_DECL {

LLVM_LIBC_FUNCTION(float16, acospif16, (float16 x)) {
using FPBits = fputil::FPBits<float16>;
FPBits xbits(x);

uint16_t x_u = xbits.uintval();
uint16_t x_abs = x_u & 0x7fff;
uint16_t x_sign = x_u >> 15;

// |x| > 0x1p0, |x| > 1, or x is NaN.
if (LIBC_UNLIKELY(x_abs > 0x3c00)) {
// acospif16(NaN) = NaN
if (xbits.is_nan()) {
if (xbits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}

return x;
}

// 1 < |x| <= +inf
fputil::raise_except_if_required(FE_INVALID);
fputil::set_errno_if_required(EDOM);

return FPBits::quiet_nan().get_val();
}

// |x| == 0x1p0, x is 1 or -1
// if x is (-)1, return 1
// if x is (+)1, return 0
if (LIBC_UNLIKELY(x_abs == 0x3c00))
return fputil::cast<float16>(x_sign ? 1.0f : 0.0f);

float xf = x;
float xsq = xf * xf;

// Degree-6 minimax polynomial coefficients of asin(x) generated by Sollya
// with: > P = fpminimax(asin(x)/(pi * x), [|0, 2, 4, 6, 8|], [|SG...|], [0,
// 0.5]);
constexpr float POLY_COEFFS[5] = {0x1.45f308p-2f, 0x1.b2900cp-5f,
0x1.897e36p-6f, 0x1.9efafcp-7f,
0x1.06d884p-6f};
// |x| <= 0x1p-1, |x| <= 0.5
if (x_abs <= 0x3800) {
// if x is 0, return 0.5
if (LIBC_UNLIKELY(x_abs == 0))
return fputil::cast<float16>(0.5f);

// Note that: acos(x) = pi/2 + asin(-x) = pi/2 - asin(x), then
// acospi(x) = 0.5 - asin(x)/pi
float interm =
fputil::polyeval(xsq, POLY_COEFFS[0], POLY_COEFFS[1], POLY_COEFFS[2],
POLY_COEFFS[3], POLY_COEFFS[4]);

return fputil::cast<float16>(fputil::multiply_add(-xf, interm, 0.5f));
}

// When |x| > 0.5, assume that 0.5 < |x| <= 1
//
// Step-by-step range-reduction proof:
// 1: Let y = asin(x), such that, x = sin(y)
// 2: From complimentary angle identity:
// x = sin(y) = cos(pi/2 - y)
// 3: Let z = pi/2 - y, such that x = cos(z)
// 4: From double angle formula; cos(2A) = 1 - 2 * sin^2(A):
// z = 2A, z/2 = A
// cos(z) = 1 - 2 * sin^2(z/2)
// 5: Make sin(z/2) subject of the formula:
// sin(z/2) = sqrt((1 - cos(z))/2)
// 6: Recall [3]; x = cos(z). Therefore:
// sin(z/2) = sqrt((1 - x)/2)
// 7: Let u = (1 - x)/2
// 8: Therefore:
// asin(sqrt(u)) = z/2
// 2 * asin(sqrt(u)) = z
// 9: Recall [3]; z = pi/2 - y. Therefore:
// y = pi/2 - z
// y = pi/2 - 2 * asin(sqrt(u))
// 10: Recall [1], y = asin(x). Therefore:
// asin(x) = pi/2 - 2 * asin(sqrt(u))
// 11: Recall that: acos(x) = pi/2 + asin(-x) = pi/2 - asin(x)
// Therefore:
// acos(x) = pi/2 - (pi/2 - 2 * asin(sqrt(u)))
// acos(x) = 2 * asin(sqrt(u))
// acospi(x) = 2 * (asin(sqrt(u)) / pi)
//
// THE RANGE REDUCTION, HOW?
// 12: Recall [7], u = (1 - x)/2
// 13: Since 0.5 < x <= 1, therefore:
// 0 <= u <= 0.25 and 0 <= sqrt(u) <= 0.5
//
// Hence, we can reuse the same [0, 0.5] domain polynomial approximation for
// Step [11] as `sqrt(u)` is in range.
// When -1 < x <= -0.5, the identity:
// acos(x) = pi - acos(-x)
// acospi(x) = 1 - acos(-x)/pi
// allows us to compute for the negative x value (lhs)
// with a positive x value instead (rhs).

float xf_abs = (xf < 0 ? -xf : xf);
float u = fputil::multiply_add(-0.5f, xf_abs, 0.5f);
float sqrt_u = fputil::sqrt<float>(u);

float asin_sqrt_u =
sqrt_u * fputil::polyeval(u, POLY_COEFFS[0], POLY_COEFFS[1],
POLY_COEFFS[2], POLY_COEFFS[3], POLY_COEFFS[4]);

// Same as acos(x), but devided the expression with pi
return fputil::cast<float16>(
x_sign ? fputil::multiply_add(-2.0f, asin_sqrt_u, 1.0f)
: 2.0f * asin_sqrt_u);
}
} // namespace LIBC_NAMESPACE_DECL
11 changes: 11 additions & 0 deletions libc/test/src/math/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2269,6 +2269,17 @@ add_fp_unittest(
libc.src.math.acosf16
)

add_fp_unittest(
acospif16_test
NEED_MPFR
SUITE
libc-math-unittests
SRCS
acospif16_test.cpp
DEPENDS
libc.src.math.acospif16
)

add_fp_unittest(
atanf_test
NEED_MPFR
Expand Down
42 changes: 42 additions & 0 deletions libc/test/src/math/acospif16_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//===-- Exhaustive test for acospif16 -------------------------------------===//
//
// 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/acospif16.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"
#include "utils/MPFRWrapper/MPFRUtils.h"

using LlvmLibcAcospif16Test = LIBC_NAMESPACE::testing::FPTest<float16>;

namespace mpfr = LIBC_NAMESPACE::testing::mpfr;

// Range: [0, Inf]
static constexpr uint16_t POS_START = 0x0000U;
static constexpr uint16_t POS_STOP = 0x7c00U;

// Range: [-Inf, 0]
static constexpr uint16_t NEG_START = 0x8000U;
static constexpr uint16_t NEG_STOP = 0xfc00U;

TEST_F(LlvmLibcAcospif16Test, PositiveRange) {
for (uint16_t v = POS_START; v <= POS_STOP; ++v) {
float16 x = FPBits(v).get_val();

EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Acospi, x,
LIBC_NAMESPACE::acospif16(x), 0.5);
}
}

TEST_F(LlvmLibcAcospif16Test, NegativeRange) {
for (uint16_t v = NEG_START; v <= NEG_STOP; ++v) {
float16 x = FPBits(v).get_val();

EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Acospi, x,
LIBC_NAMESPACE::acospif16(x), 0.5);
}
}
11 changes: 11 additions & 0 deletions libc/test/src/math/smoke/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4040,6 +4040,17 @@ add_fp_unittest(
libc.src.math.acosf16
)

add_fp_unittest(
acospif16_test
SUITE
libc-math-smoke-tests
SRCS
acospif16_test.cpp
DEPENDS
libc.src.errno.errno
libc.src.math.acospif16
)

add_fp_unittest(
atanf_test
SUITE
Expand Down
38 changes: 38 additions & 0 deletions libc/test/src/math/smoke/acospif16_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//===-- Unittests for acospif16 -------------------------------------------===//
//
// 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/acospif16.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"

using LlvmLibcAcospif16Test = LIBC_NAMESPACE::testing::FPTest<float16>;
TEST_F(LlvmLibcAcospif16Test, SpecialNumbers) {
LIBC_NAMESPACE::libc_errno = 0;
EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::acospif16(aNaN));
EXPECT_MATH_ERRNO(0);

EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, LIBC_NAMESPACE::acospif16(sNaN),
FE_INVALID);
EXPECT_MATH_ERRNO(0);

EXPECT_FP_EQ(zero, LIBC_NAMESPACE::acospif16(1.0f));
EXPECT_MATH_ERRNO(0);

EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::acospif16(inf));
EXPECT_MATH_ERRNO(EDOM);

EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::acospif16(neg_inf));
EXPECT_MATH_ERRNO(EDOM);

EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::acospif16(2.0f));
EXPECT_MATH_ERRNO(EDOM);

EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::acospif16(-2.0f));
EXPECT_MATH_ERRNO(EDOM);
}
17 changes: 17 additions & 0 deletions libc/utils/MPFRWrapper/MPCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,23 @@ MPFRNumber MPFRNumber::acosh() const {
return result;
}

MPFRNumber MPFRNumber::acospi() const {
MPFRNumber result(*this);

#if MPFR_VERSION_MAJOR > 4 || \
(MPFR_VERSION_MAJOR == 4 && MPFR_VERSION_MINOR >= 2)
mpfr_acospi(result.value, value, mpfr_rounding);
return result;
#else
MPFRNumber value_acos(0.0, 1280);
mpfr_acos(value_acos.value, value, MPFR_RNDN);
MPFRNumber value_pi(0.0, 1280);
mpfr_const_pi(value_pi.value, MPFR_RNDN);
mpfr_div(result.value, value_acos.value, value_pi.value, mpfr_rounding);
return result;
#endif
}

MPFRNumber MPFRNumber::add(const MPFRNumber &b) const {
MPFRNumber result(*this);
mpfr_add(result.value, value, b.value, mpfr_rounding);
Expand Down
1 change: 1 addition & 0 deletions libc/utils/MPFRWrapper/MPCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ class MPFRNumber {
MPFRNumber abs() const;
MPFRNumber acos() const;
MPFRNumber acosh() const;
MPFRNumber acospi() const;
MPFRNumber add(const MPFRNumber &b) const;
MPFRNumber asin() const;
MPFRNumber asinh() const;
Expand Down
2 changes: 2 additions & 0 deletions libc/utils/MPFRWrapper/MPFRUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ unary_operation(Operation op, InputType input, unsigned int precision,
return mpfrInput.acos();
case Operation::Acosh:
return mpfrInput.acosh();
case Operation::Acospi:
return mpfrInput.acospi();
case Operation::Asin:
return mpfrInput.asin();
case Operation::Asinh:
Expand Down
1 change: 1 addition & 0 deletions libc/utils/MPFRWrapper/MPFRUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ enum class Operation : int {
Abs,
Acos,
Acosh,
Acospi,
Asin,
Asinh,
Atan,
Expand Down
Loading