Skip to content

Commit 8078665

Browse files
authored
[libc][math][c23] Add hypotf16 function (#131991)
Implement hypot for Float16 along with tests.
1 parent c7572ae commit 8078665

File tree

18 files changed

+321
-5
lines changed

18 files changed

+321
-5
lines changed

libc/config/linux/x86_64/entrypoints.txt

+1
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
706706
libc.src.math.fromfpf16
707707
libc.src.math.fromfpxf16
708708
libc.src.math.getpayloadf16
709+
libc.src.math.hypotf16
709710
libc.src.math.ilogbf16
710711
libc.src.math.iscanonicalf16
711712
libc.src.math.issignalingf16

libc/docs/headers/math/index.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ Higher Math Functions
305305
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
306306
| fsqrt | N/A | |check| | |check| | N/A | |check|\* | 7.12.14.6 | F.10.11 |
307307
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
308-
| hypot | |check| | |check| | | | | 7.12.7.4 | F.10.4.4 |
308+
| hypot | |check| | |check| | | |check| | | 7.12.7.4 | F.10.4.4 |
309309
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
310310
| lgamma | | | | | | 7.12.8.3 | F.10.5.3 |
311311
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+

libc/include/math.yaml

+8
Original file line numberDiff line numberDiff line change
@@ -1395,6 +1395,14 @@ functions:
13951395
arguments:
13961396
- type: float
13971397
- type: float
1398+
- name: hypotf16
1399+
standards:
1400+
- stdc
1401+
return_type: _Float16
1402+
arguments:
1403+
- type: _Float16
1404+
- type: _Float16
1405+
guard: LIBC_TYPES_HAS_FLOAT16
13981406
- name: ilogb
13991407
standards:
14001408
- stdc

libc/src/__support/FPUtil/Hypot.h

+5-3
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ LIBC_INLINE T find_leading_one(T mant, int &shift_length) {
3030
if (mant > 0) {
3131
shift_length = (sizeof(mant) * 8) - 1 - cpp::countl_zero(mant);
3232
}
33-
return T(1) << shift_length;
33+
return static_cast<T>((T(1) << shift_length));
3434
}
3535

3636
} // namespace internal
@@ -207,8 +207,10 @@ LIBC_INLINE T hypot(T x, T y) {
207207

208208
for (StorageType current_bit = leading_one >> 1; current_bit;
209209
current_bit >>= 1) {
210-
r = (r << 1) + ((tail_bits & current_bit) ? 1 : 0);
211-
StorageType tmp = (y_new << 1) + current_bit; // 2*y_new(n - 1) + 2^(-n)
210+
r = static_cast<StorageType>((r << 1)) +
211+
((tail_bits & current_bit) ? 1 : 0);
212+
StorageType tmp = static_cast<StorageType>((y_new << 1)) +
213+
current_bit; // 2*y_new(n - 1) + 2^(-n)
212214
if (r >= tmp) {
213215
r -= tmp;
214216
y_new += current_bit;

libc/src/__support/FPUtil/cast.h

+3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818

1919
namespace LIBC_NAMESPACE::fputil {
2020

21+
// TODO: Add optimization for known good targets with fast
22+
// float to float16 conversion:
23+
// https://github.com./llvm/llvm-project/issues/133517
2124
template <typename OutType, typename InType>
2225
LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_floating_point_v<OutType> &&
2326
cpp::is_floating_point_v<InType>,

libc/src/math/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ add_math_entrypoint_object(getpayloadf128)
313313

314314
add_math_entrypoint_object(hypot)
315315
add_math_entrypoint_object(hypotf)
316+
add_math_entrypoint_object(hypotf16)
316317

317318
add_math_entrypoint_object(ilogb)
318319
add_math_entrypoint_object(ilogbf)

libc/src/math/generic/CMakeLists.txt

+16
Original file line numberDiff line numberDiff line change
@@ -3105,6 +3105,22 @@ add_entrypoint_object(
31053105
libc.src.__support.macros.optimization
31063106
)
31073107

3108+
add_entrypoint_object(
3109+
hypotf16
3110+
SRCS
3111+
hypotf16.cpp
3112+
HDRS
3113+
../hypotf16.h
3114+
DEPENDS
3115+
libc.src.__support.FPUtil.fenv_impl
3116+
libc.src.__support.FPUtil.fp_bits
3117+
libc.src.__support.FPUtil.cast
3118+
libc.src.__support.FPUtil.multiply_add
3119+
libc.src.__support.FPUtil.sqrt
3120+
libc.src.__support.macros.optimization
3121+
libc.src.__support.macros.properties.types
3122+
)
3123+
31083124
add_entrypoint_object(
31093125
fdim
31103126
SRCS

libc/src/math/generic/hypotf16.cpp

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
//===-- Implementation of hypotf16 function -------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "src/math/hypotf16.h"
10+
#include "src/__support/FPUtil/FEnvImpl.h"
11+
#include "src/__support/FPUtil/FPBits.h"
12+
#include "src/__support/FPUtil/cast.h"
13+
#include "src/__support/FPUtil/multiply_add.h"
14+
#include "src/__support/FPUtil/sqrt.h"
15+
#include "src/__support/common.h"
16+
#include "src/__support/macros/optimization.h"
17+
#include "src/__support/macros/properties/types.h"
18+
19+
namespace LIBC_NAMESPACE_DECL {
20+
21+
// For targets where conversion from float to float16 has to be
22+
// emulated, fputil::hypot<float16> is faster
23+
LLVM_LIBC_FUNCTION(float16, hypotf16, (float16 x, float16 y)) {
24+
using FloatBits = fputil::FPBits<float>;
25+
using FPBits = fputil::FPBits<float16>;
26+
27+
FPBits x_abs = FPBits(x).abs();
28+
FPBits y_abs = FPBits(y).abs();
29+
30+
bool x_abs_larger = x_abs.uintval() >= y_abs.uintval();
31+
32+
FPBits a_bits = x_abs_larger ? x_abs : y_abs;
33+
FPBits b_bits = x_abs_larger ? y_abs : x_abs;
34+
35+
uint16_t a_u = a_bits.uintval();
36+
uint16_t b_u = b_bits.uintval();
37+
38+
// Note: replacing `a_u >= FPBits::EXP_MASK` with `a_bits.is_inf_or_nan()`
39+
// generates extra exponent bit masking instructions on x86-64.
40+
if (LIBC_UNLIKELY(a_u >= FPBits::EXP_MASK)) {
41+
// x or y is inf or nan
42+
if (a_bits.is_signaling_nan() || b_bits.is_signaling_nan()) {
43+
fputil::raise_except_if_required(FE_INVALID);
44+
return FPBits::quiet_nan().get_val();
45+
}
46+
if (a_bits.is_inf() || b_bits.is_inf())
47+
return FPBits::inf().get_val();
48+
return a_bits.get_val();
49+
}
50+
51+
if (LIBC_UNLIKELY(a_u - b_u >=
52+
static_cast<uint16_t>((FPBits::FRACTION_LEN + 2)
53+
<< FPBits::FRACTION_LEN)))
54+
return x_abs.get_val() + y_abs.get_val();
55+
56+
float af = fputil::cast<float>(a_bits.get_val());
57+
float bf = fputil::cast<float>(b_bits.get_val());
58+
59+
// These squares are exact.
60+
float a_sq = af * af;
61+
float sum_sq = fputil::multiply_add(bf, bf, a_sq);
62+
63+
FloatBits result(fputil::sqrt<float>(sum_sq));
64+
uint32_t r_u = result.uintval();
65+
66+
// If any of the sticky bits of the result are non-zero, except the LSB, then
67+
// the rounded result is correct.
68+
if (LIBC_UNLIKELY(((r_u + 1) & 0x0000'0FFE) == 0)) {
69+
float r_d = result.get_val();
70+
71+
// Perform rounding correction.
72+
float sum_sq_lo = fputil::multiply_add(bf, bf, a_sq - sum_sq);
73+
float err = sum_sq_lo - fputil::multiply_add(r_d, r_d, -sum_sq);
74+
75+
if (err > 0) {
76+
r_u |= 1;
77+
} else if ((err < 0) && (r_u & 1) == 0) {
78+
r_u -= 1;
79+
} else if ((r_u & 0x0000'1FFF) == 0) {
80+
// The rounded result is exact.
81+
fputil::clear_except_if_required(FE_INEXACT);
82+
}
83+
return fputil::cast<float16>(FloatBits(r_u).get_val());
84+
}
85+
86+
return fputil::cast<float16>(result.get_val());
87+
}
88+
89+
} // namespace LIBC_NAMESPACE_DECL

libc/src/math/hypotf16.h

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//===-- Implementation header for hypotf16 ----------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC_MATH_HYPOTF16_H
10+
#define LLVM_LIBC_SRC_MATH_HYPOTF16_H
11+
12+
#include "src/__support/macros/config.h"
13+
#include "src/__support/macros/properties/types.h"
14+
15+
namespace LIBC_NAMESPACE_DECL {
16+
17+
float16 hypotf16(float16 x, float16 y);
18+
19+
} // namespace LIBC_NAMESPACE_DECL
20+
21+
#endif // LLVM_LIBC_SRC_MATH_HYPOTF16_H

libc/test/src/math/CMakeLists.txt

+11
Original file line numberDiff line numberDiff line change
@@ -1701,6 +1701,17 @@ add_fp_unittest(
17011701
libc.src.__support.FPUtil.fp_bits
17021702
)
17031703

1704+
add_fp_unittest(
1705+
hypotf16_test
1706+
NEED_MPFR
1707+
SUITE
1708+
libc-math-unittests
1709+
SRCS
1710+
hypotf16_test.cpp
1711+
DEPENDS
1712+
libc.src.math.hypotf16
1713+
)
1714+
17041715
add_fp_unittest(
17051716
nextafter_test
17061717
SUITE

libc/test/src/math/HypotTest.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ class HypotTestTemplate : public LIBC_NAMESPACE::testing::FEnvSafeTest {
7373
constexpr StorageType COUNT = 10'001;
7474
for (unsigned scale = 0; scale < 4; ++scale) {
7575
StorageType max_value = MAX_SUBNORMAL << scale;
76-
StorageType step = (max_value - MIN_SUBNORMAL) / COUNT;
76+
StorageType step = (max_value - MIN_SUBNORMAL) / COUNT + 1;
7777
for (int signs = 0; signs < 4; ++signs) {
7878
for (StorageType v = MIN_SUBNORMAL, w = max_value;
7979
v <= max_value && w >= MIN_SUBNORMAL; v += step, w -= step) {

libc/test/src/math/exhaustive/CMakeLists.txt

+18
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,24 @@ add_fp_unittest(
314314
-lpthread
315315
)
316316

317+
add_fp_unittest(
318+
hypotf16_test
319+
NO_RUN_POSTBUILD
320+
NEED_MPFR
321+
SUITE
322+
libc_math_exhaustive_tests
323+
SRCS
324+
hypotf16_test.cpp
325+
COMPILE_OPTIONS
326+
${libc_opt_high_flag}
327+
DEPENDS
328+
.exhaustive_test
329+
libc.src.math.hypotf16
330+
libc.src.__support.FPUtil.fp_bits
331+
LINK_LIBRARIES
332+
-lpthread
333+
)
334+
317335
add_fp_unittest(
318336
fmod_generic_impl_test
319337
NO_RUN_POSTBUILD
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//===-- Exhaustive test for hypotf16 --------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "exhaustive_test.h"
10+
#include "src/__support/FPUtil/FPBits.h"
11+
#include "src/__support/FPUtil/Hypot.h"
12+
#include "src/math/hypotf16.h"
13+
#include "test/UnitTest/FPMatcher.h"
14+
#include "utils/MPFRWrapper/MPFRUtils.h"
15+
16+
namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
17+
18+
struct Hypotf16Checker : public virtual LIBC_NAMESPACE::testing::Test {
19+
using FloatType = float16;
20+
using FPBits = LIBC_NAMESPACE::fputil::FPBits<float16>;
21+
using StorageType = typename FPBits::StorageType;
22+
23+
uint64_t check(uint16_t x_start, uint16_t x_stop, uint16_t y_start,
24+
uint16_t y_stop, mpfr::RoundingMode rounding) {
25+
mpfr::ForceRoundingMode r(rounding);
26+
if (!r.success)
27+
return true;
28+
uint16_t xbits = x_start;
29+
uint64_t failed = 0;
30+
do {
31+
float16 x = FPBits(xbits).get_val();
32+
uint16_t ybits = xbits;
33+
do {
34+
float16 y = FPBits(ybits).get_val();
35+
bool correct = TEST_FP_EQ(LIBC_NAMESPACE::fputil::hypot<float16>(x, y),
36+
LIBC_NAMESPACE::hypotf16(x, y));
37+
// Using MPFR will be much slower.
38+
// mpfr::BinaryInput<float16> input{x, y};
39+
// bool correct = TEST_MPFR_MATCH_ROUNDING_SILENTLY(
40+
// mpfr::Operation::Hypot, input, LIBC_NAMESPACE::hypotf16(x, y),
41+
// 0.5,
42+
// rounding);
43+
failed += (!correct);
44+
} while (ybits++ < y_stop);
45+
} while (xbits++ < x_stop);
46+
return failed;
47+
}
48+
};
49+
50+
using LlvmLibcHypotf16ExhaustiveTest =
51+
LlvmLibcExhaustiveMathTest<Hypotf16Checker, 1 << 2>;
52+
53+
// Range of both inputs: [0, inf]
54+
static constexpr uint16_t POS_START = 0x0000U;
55+
static constexpr uint16_t POS_STOP = 0x7C00U;
56+
57+
TEST_F(LlvmLibcHypotf16ExhaustiveTest, PositiveRange) {
58+
test_full_range_all_roundings(POS_START, POS_STOP, POS_START, POS_STOP);
59+
}
60+
61+
// Range of both inputs: [-0, -inf]
62+
static constexpr uint16_t NEG_START = 0x8000U;
63+
static constexpr uint16_t NEG_STOP = 0xFC00U;
64+
65+
TEST_F(LlvmLibcHypotf16ExhaustiveTest, NegativeRange) {
66+
test_full_range_all_roundings(NEG_START, NEG_STOP, NEG_START, NEG_STOP);
67+
}

libc/test/src/math/hypotf16_test.cpp

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//===-- Unittests for hypotf16 --------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "HypotTest.h"
10+
11+
#include "src/math/hypotf16.h"
12+
13+
using LlvmLibcHypotf16Test = HypotTestTemplate<float16>;
14+
15+
TEST_F(LlvmLibcHypotf16Test, SubnormalRange) {
16+
test_subnormal_range(&LIBC_NAMESPACE::hypotf16);
17+
}
18+
19+
TEST_F(LlvmLibcHypotf16Test, NormalRange) {
20+
test_normal_range(&LIBC_NAMESPACE::hypotf16);
21+
}

libc/test/src/math/performance_testing/CMakeLists.txt

+12
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,18 @@ add_perf_binary(
340340
-fno-builtin
341341
)
342342

343+
add_perf_binary(
344+
hypotf16_perf
345+
SRCS
346+
hypotf16_perf.cpp
347+
DEPENDS
348+
.binary_op_single_output_diff
349+
libc.src.math.hypotf16
350+
libc.src.__support.FPUtil.fp_bits
351+
COMPILE_OPTIONS
352+
-fno-builtin
353+
)
354+
343355
add_perf_binary(
344356
hypotf_perf
345357
SRCS
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//===-- Differential test for hypotf16 ------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "BinaryOpSingleOutputPerf.h"
10+
11+
#include "src/__support/FPUtil/Hypot.h"
12+
#include "src/math/hypotf16.h"
13+
14+
BINARY_OP_SINGLE_OUTPUT_PERF(float16, float16, LIBC_NAMESPACE::hypotf16,
15+
LIBC_NAMESPACE::fputil::hypot<float16>,
16+
"hypotf16_perf.log")

0 commit comments

Comments
 (0)