Skip to content

Commit 2f0d352

Browse files
committed
[libc][math][c23] Add log2f16 C23 math function
Part of #95250.
1 parent efc9cca commit 2f0d352

File tree

13 files changed

+318
-1
lines changed

13 files changed

+318
-1
lines changed

libc/config/gpu/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
531531
libc.src.math.llogbf16
532532
libc.src.math.llrintf16
533533
libc.src.math.llroundf16
534+
libc.src.math.log2f16
534535
libc.src.math.logbf16
535536
libc.src.math.logf16
536537
libc.src.math.lrintf16

libc/config/linux/x86_64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
642642
libc.src.math.llogbf16
643643
libc.src.math.llrintf16
644644
libc.src.math.llroundf16
645+
libc.src.math.log2f16
645646
libc.src.math.logbf16
646647
libc.src.math.logf16
647648
libc.src.math.lrintf16

libc/docs/math/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ Higher Math Functions
314314
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
315315
| log1p | |check| | |check| | | | | 7.12.6.14 | F.10.3.14 |
316316
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
317-
| log2 | |check| | |check| | | | | 7.12.6.15 | F.10.3.15 |
317+
| log2 | |check| | |check| | | |check| | | 7.12.6.15 | F.10.3.15 |
318318
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
319319
| log2p1 | | | | | | 7.12.6.16 | F.10.3.16 |
320320
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+

libc/spec/stdc.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,7 @@ def StdC : StandardSpec<"stdc"> {
562562

563563
FunctionSpec<"log2", RetValSpec<DoubleType>, [ArgSpec<DoubleType>]>,
564564
FunctionSpec<"log2f", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,
565+
GuardedFunctionSpec<"log2f16", RetValSpec<Float16Type>, [ArgSpec<Float16Type>], "LIBC_TYPES_HAS_FLOAT16">,
565566

566567
FunctionSpec<"log", RetValSpec<DoubleType>, [ArgSpec<DoubleType>]>,
567568
FunctionSpec<"logf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,

libc/src/math/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ add_math_entrypoint_object(log1pf)
328328

329329
add_math_entrypoint_object(log2)
330330
add_math_entrypoint_object(log2f)
331+
add_math_entrypoint_object(log2f16)
331332

332333
add_math_entrypoint_object(log)
333334
add_math_entrypoint_object(logf)

libc/src/math/generic/CMakeLists.txt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2171,6 +2171,27 @@ add_entrypoint_object(
21712171
-O3
21722172
)
21732173

2174+
add_entrypoint_object(
2175+
log2f16
2176+
SRCS
2177+
log2f16.cpp
2178+
HDRS
2179+
../log2f16.h
2180+
DEPENDS
2181+
.expxf16
2182+
libc.hdr.errno_macros
2183+
libc.hdr.fenv_macros
2184+
libc.src.__support.FPUtil.except_value_utils
2185+
libc.src.__support.FPUtil.fenv_impl
2186+
libc.src.__support.FPUtil.fp_bits
2187+
libc.src.__support.FPUtil.multiply_add
2188+
libc.src.__support.FPUtil.polyeval
2189+
libc.src.__support.macros.optimization
2190+
libc.src.__support.macros.properties.cpu_features
2191+
COMPILE_OPTIONS
2192+
-O3
2193+
)
2194+
21742195
add_entrypoint_object(
21752196
log
21762197
SRCS

libc/src/math/generic/expxf16.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,20 @@ constexpr cpp::array<float, 32> LOGF_F = {
300300
0x1.52a2d2p-1, 0x1.5ad404p-1,
301301
};
302302

303+
// Generated by Sollya with the following commands:
304+
// > display = hexadecimal;
305+
// > for i from 0 to 31 do print(round(log2(1 + i * 2^-5), SG, RN));
306+
constexpr cpp::array<float, 32> LOG2F_F = {
307+
0x0p+0f, 0x1.6bad38p-5f, 0x1.663f7p-4f, 0x1.08c588p-3f,
308+
0x1.5c01a4p-3f, 0x1.acf5e2p-3f, 0x1.fbc16cp-3f, 0x1.24407ap-2f,
309+
0x1.49a784p-2f, 0x1.6e221cp-2f, 0x1.91bba8p-2f, 0x1.b47ecp-2f,
310+
0x1.d6753ep-2f, 0x1.f7a856p-2f, 0x1.0c105p-1f, 0x1.1bf312p-1f,
311+
0x1.2b8034p-1f, 0x1.3abb4p-1f, 0x1.49a784p-1f, 0x1.584822p-1f,
312+
0x1.66a008p-1f, 0x1.74b1fep-1f, 0x1.82809ep-1f, 0x1.900e62p-1f,
313+
0x1.9d5dap-1f, 0x1.aa709p-1f, 0x1.b74948p-1f, 0x1.c3e9cap-1f,
314+
0x1.d053f6p-1f, 0x1.dc899ap-1f, 0x1.e88c6cp-1f, 0x1.f45e08p-1f,
315+
};
316+
303317
// Generated by Sollya with the following commands:
304318
// > display = hexadecimal;
305319
// > for i from 0 to 31 do print(round(1 / (1 + i * 2^-5), SG, RN));

libc/src/math/generic/log2f16.cpp

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
//===-- Implementation of log2f16 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/log2f16.h"
10+
#include "expxf16.h"
11+
#include "hdr/errno_macros.h"
12+
#include "hdr/fenv_macros.h"
13+
#include "src/__support/FPUtil/FEnvImpl.h"
14+
#include "src/__support/FPUtil/FPBits.h"
15+
#include "src/__support/FPUtil/PolyEval.h"
16+
#include "src/__support/FPUtil/except_value_utils.h"
17+
#include "src/__support/FPUtil/multiply_add.h"
18+
#include "src/__support/common.h"
19+
#include "src/__support/macros/config.h"
20+
#include "src/__support/macros/optimization.h"
21+
#include "src/__support/macros/properties/cpu_features.h"
22+
23+
namespace LIBC_NAMESPACE_DECL {
24+
25+
#ifdef LIBC_TARGET_CPU_HAS_FMA
26+
static constexpr size_t N_LOG2F16_EXCEPTS = 2;
27+
#else
28+
static constexpr size_t N_LOG2F16_EXCEPTS = 9;
29+
#endif
30+
31+
static constexpr fputil::ExceptValues<float16, N_LOG2F16_EXCEPTS>
32+
LOG2F16_EXCEPTS = {{
33+
#ifndef LIBC_TARGET_CPU_HAS_FMA
34+
// x = 0x1.224p-1, log2f16(x) = -0x1.a34p-1 (RZ)
35+
{0x3889U, 0xba8dU, 0U, 1U, 0U},
36+
// x = 0x1.e34p-1, log2f16(x) = -0x1.558p-4 (RZ)
37+
{0x3b8dU, 0xad56U, 0U, 1U, 0U},
38+
#endif
39+
// x = 0x1.e8cp-1, log2f16(x) = -0x1.128p-4 (RZ)
40+
{0x3ba3U, 0xac4aU, 0U, 1U, 0U},
41+
#ifndef LIBC_TARGET_CPU_HAS_FMA
42+
// x = 0x1.f98p-1, log2f16(x) = -0x1.2ep-6 (RZ)
43+
{0x3be6U, 0xa4b8U, 0U, 1U, 0U},
44+
// x = 0x1.facp-1, log2f16(x) = -0x1.e7p-7 (RZ)
45+
{0x3bebU, 0xa39cU, 0U, 1U, 1U},
46+
#endif
47+
// x = 0x1.fb4p-1, log2f16(x) = -0x1.b88p-7 (RZ)
48+
{0x3bedU, 0xa2e2U, 0U, 1U, 1U},
49+
#ifndef LIBC_TARGET_CPU_HAS_FMA
50+
// x = 0x1.fecp-1, log2f16(x) = -0x1.cep-9 (RZ)
51+
{0x3bfbU, 0x9b38U, 0U, 1U, 1U},
52+
// x = 0x1.ffcp-1, log2f16(x) = -0x1.714p-11 (RZ)
53+
{0x3bffU, 0x91c5U, 0U, 1U, 1U},
54+
// x = 0x1.224p+0, log2f16(x) = 0x1.72cp-3 (RZ)
55+
{0x3c89U, 0x31cbU, 1U, 0U, 1U},
56+
#endif
57+
}};
58+
59+
LLVM_LIBC_FUNCTION(float16, log2f16, (float16 x)) {
60+
using FPBits = fputil::FPBits<float16>;
61+
FPBits x_bits(x);
62+
63+
uint16_t x_u = x_bits.uintval();
64+
65+
// If x <= 0, or x is 1, or x is +inf, or x is NaN.
66+
if (LIBC_UNLIKELY(x_u == 0U || x_u == 0x3c00U || x_u >= 0x7c00U)) {
67+
// log2(NaN) = NaN
68+
if (x_bits.is_nan()) {
69+
if (x_bits.is_signaling_nan()) {
70+
fputil::raise_except_if_required(FE_INVALID);
71+
return FPBits::quiet_nan().get_val();
72+
}
73+
74+
return x;
75+
}
76+
77+
// log2(+/-0) = −inf
78+
if ((x_u & 0x7fffU) == 0U) {
79+
fputil::raise_except_if_required(FE_DIVBYZERO);
80+
return FPBits::inf(Sign::NEG).get_val();
81+
}
82+
83+
if (x_u == 0x3c00U)
84+
return FPBits::zero().get_val();
85+
86+
// When x < 0.
87+
if (x_u > 0x8000U) {
88+
fputil::set_errno_if_required(EDOM);
89+
fputil::raise_except_if_required(FE_INVALID);
90+
return FPBits::quiet_nan().get_val();
91+
}
92+
93+
// log2(+inf) = +inf
94+
return FPBits::inf().get_val();
95+
}
96+
97+
if (auto r = LOG2F16_EXCEPTS.lookup(x_u); LIBC_UNLIKELY(r.has_value()))
98+
return r.value();
99+
100+
// To compute log2(x), we perform the following range reduction:
101+
// x = 2^m * 1.mant,
102+
// log2(x) = m + log2(1.mant).
103+
// To compute log2(1.mant), let f be the highest 6 bits including the hidden
104+
// bit, and d be the difference (1.mant - f), i.e., the remaining 5 bits of
105+
// the mantissa, then:
106+
// log2(1.mant) = log2(f) + log2(1.mant / f)
107+
// = log2(f) + log2(1 + d/f)
108+
// since d/f is sufficiently small.
109+
// We store log2(f) and 1/f in the lookup tables LOG2F_F and ONE_OVER_F
110+
// respectively.
111+
112+
int m = -FPBits::EXP_BIAS;
113+
114+
// When x is subnormal.
115+
if ((x_u & FPBits::EXP_MASK) == 0U) {
116+
// Normalize x.
117+
x_bits = FPBits(x_bits.get_val() *
118+
static_cast<float16>((1U << FPBits::FRACTION_LEN)));
119+
x_u = x_bits.uintval();
120+
m -= FPBits::FRACTION_LEN;
121+
}
122+
123+
uint16_t mant = x_bits.get_mantissa();
124+
// Leading 10 - 5 = 5 bits of the mantissa.
125+
int f = mant >> 5;
126+
// Unbiased exponent.
127+
m += x_u >> FPBits::FRACTION_LEN;
128+
129+
// Set bits to 1.mant instead of 2^m * 1.mant.
130+
x_bits.set_biased_exponent(FPBits::EXP_BIAS);
131+
float mant_f = x_bits.get_val();
132+
// v = 1.mant * 1/f - 1 = d/f
133+
float v = fputil::multiply_add(mant_f, ONE_OVER_F[f], -1.0f);
134+
135+
// Degree-3 minimax polynomial generated by Sollya with the following
136+
// commands:
137+
// > display = hexadecimal;
138+
// > P = fpminimax(log2(1 + x)/x, 2, [|SG...|], [-2^-5, 2^-5]);
139+
// > x * P;
140+
float log2p1_d_over_f =
141+
v * fputil::polyeval(v, 0x1.715476p+0f, -0x1.71771ap-1f, 0x1.ecb38ep-2f);
142+
// log2(1.mant) = log2(f) + log2(1 + d/f)
143+
float log2_1_mant = LOG2F_F[f] + log2p1_d_over_f;
144+
return static_cast<float16>(static_cast<float>(m) + log2_1_mant);
145+
}
146+
147+
} // namespace LIBC_NAMESPACE_DECL

libc/src/math/log2f16.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//===-- Implementation header for log2f16 -----------------------*- 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_LOG2F16_H
10+
#define LLVM_LIBC_SRC_MATH_LOG2F16_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 log2f16(float16 x);
18+
19+
} // namespace LIBC_NAMESPACE_DECL
20+
21+
#endif // LLVM_LIBC_SRC_MATH_LOG2F16_H

libc/test/src/math/CMakeLists.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1798,6 +1798,17 @@ add_fp_unittest(
17981798
libc.src.__support.FPUtil.fp_bits
17991799
)
18001800

1801+
add_fp_unittest(
1802+
log2f16_test
1803+
NEED_MPFR
1804+
SUITE
1805+
libc-math-unittests
1806+
SRCS
1807+
log2f16_test.cpp
1808+
DEPENDS
1809+
libc.src.math.log2f16
1810+
)
1811+
18011812
add_fp_unittest(
18021813
log10_test
18031814
NEED_MPFR

libc/test/src/math/log2f16_test.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//===-- Exhaustive test for log2f16 ---------------------------------------===//
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/log2f16.h"
10+
#include "test/UnitTest/FPMatcher.h"
11+
#include "test/UnitTest/Test.h"
12+
#include "utils/MPFRWrapper/MPFRUtils.h"
13+
14+
using LlvmLibcLog2f16Test = LIBC_NAMESPACE::testing::FPTest<float16>;
15+
16+
namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
17+
18+
// Range: [0, Inf];
19+
static constexpr uint16_t POS_START = 0x0000U;
20+
static constexpr uint16_t POS_STOP = 0x7c00U;
21+
22+
// Range: [-Inf, 0];
23+
static constexpr uint16_t NEG_START = 0x8000U;
24+
static constexpr uint16_t NEG_STOP = 0xfc00U;
25+
26+
TEST_F(LlvmLibcLog2f16Test, PositiveRange) {
27+
for (uint16_t v = POS_START; v <= POS_STOP; ++v) {
28+
float16 x = FPBits(v).get_val();
29+
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Log2, x,
30+
LIBC_NAMESPACE::log2f16(x), 0.5);
31+
}
32+
}
33+
34+
TEST_F(LlvmLibcLog2f16Test, NegativeRange) {
35+
for (uint16_t v = NEG_START; v <= NEG_STOP; ++v) {
36+
float16 x = FPBits(v).get_val();
37+
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Log2, x,
38+
LIBC_NAMESPACE::log2f16(x), 0.5);
39+
}
40+
}

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3444,6 +3444,18 @@ add_fp_unittest(
34443444
libc.src.__support.FPUtil.fp_bits
34453445
)
34463446

3447+
add_fp_unittest(
3448+
log2f16_test
3449+
SUITE
3450+
libc-math-smoke-tests
3451+
SRCS
3452+
log2f16_test.cpp
3453+
DEPENDS
3454+
libc.hdr.fenv_macros
3455+
libc.src.errno.errno
3456+
libc.src.math.log2f16
3457+
)
3458+
34473459
add_fp_unittest(
34483460
log10_test
34493461
SUITE
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//===-- Unittests for log2f16 ---------------------------------------------===//
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 "hdr/fenv_macros.h"
10+
#include "src/errno/libc_errno.h"
11+
#include "src/math/log2f16.h"
12+
#include "test/UnitTest/FPMatcher.h"
13+
#include "test/UnitTest/Test.h"
14+
15+
using LlvmLibcLog2f16Test = LIBC_NAMESPACE::testing::FPTest<float16>;
16+
17+
TEST_F(LlvmLibcLog2f16Test, SpecialNumbers) {
18+
LIBC_NAMESPACE::libc_errno = 0;
19+
20+
EXPECT_FP_EQ_ALL_ROUNDING(aNaN, LIBC_NAMESPACE::log2f16(aNaN));
21+
EXPECT_MATH_ERRNO(0);
22+
23+
EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, LIBC_NAMESPACE::log2f16(sNaN), FE_INVALID);
24+
EXPECT_MATH_ERRNO(0);
25+
26+
EXPECT_FP_EQ_ALL_ROUNDING(inf, LIBC_NAMESPACE::log2f16(inf));
27+
EXPECT_MATH_ERRNO(0);
28+
29+
EXPECT_FP_EQ_ALL_ROUNDING(aNaN, LIBC_NAMESPACE::log2f16(neg_inf));
30+
EXPECT_MATH_ERRNO(EDOM);
31+
32+
EXPECT_FP_EQ_WITH_EXCEPTION_ALL_ROUNDING(
33+
neg_inf, LIBC_NAMESPACE::log2f16(zero), FE_DIVBYZERO);
34+
EXPECT_MATH_ERRNO(0);
35+
36+
EXPECT_FP_EQ_WITH_EXCEPTION_ALL_ROUNDING(
37+
neg_inf, LIBC_NAMESPACE::log2f16(neg_zero), FE_DIVBYZERO);
38+
EXPECT_MATH_ERRNO(0);
39+
40+
EXPECT_FP_EQ_ALL_ROUNDING(zero,
41+
LIBC_NAMESPACE::log2f16(static_cast<float16>(1.0)));
42+
EXPECT_MATH_ERRNO(0);
43+
44+
EXPECT_FP_EQ_ALL_ROUNDING(
45+
aNaN, LIBC_NAMESPACE::log2f16(static_cast<float16>(-1.0)));
46+
EXPECT_MATH_ERRNO(EDOM);
47+
}

0 commit comments

Comments
 (0)