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 logf16 C23 math function #106072

Merged
merged 5 commits into from
Oct 18, 2024
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/gpu/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
libc.src.math.llrintf16
libc.src.math.llroundf16
libc.src.math.logbf16
libc.src.math.logf16
libc.src.math.lrintf16
libc.src.math.lroundf16
libc.src.math.modff16
Expand Down
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 @@ -661,6 +661,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
libc.src.math.llrintf16
libc.src.math.llroundf16
libc.src.math.logbf16
libc.src.math.logf16
libc.src.math.lrintf16
libc.src.math.lroundf16
libc.src.math.modff16
Expand Down
2 changes: 1 addition & 1 deletion libc/docs/math/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ Higher Math Functions
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
| lgamma | | | | | | 7.12.8.3 | F.10.5.3 |
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
| log | |check| | |check| | | | | 7.12.6.11 | F.10.3.11 |
| log | |check| | |check| | | |check| | | 7.12.6.11 | F.10.3.11 |
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
| log10 | |check| | |check| | | | | 7.12.6.12 | F.10.3.12 |
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
Expand Down
1 change: 1 addition & 0 deletions libc/spec/stdc.td
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,7 @@ def StdC : StandardSpec<"stdc"> {

FunctionSpec<"log", RetValSpec<DoubleType>, [ArgSpec<DoubleType>]>,
FunctionSpec<"logf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,
GuardedFunctionSpec<"logf16", RetValSpec<Float16Type>, [ArgSpec<Float16Type>], "LIBC_TYPES_HAS_FLOAT16">,

FunctionSpec<"logb", RetValSpec<DoubleType>, [ArgSpec<DoubleType>]>,
FunctionSpec<"logbf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,
Expand Down
1 change: 1 addition & 0 deletions libc/src/math/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@ add_math_entrypoint_object(log2f)

add_math_entrypoint_object(log)
add_math_entrypoint_object(logf)
add_math_entrypoint_object(logf16)

add_math_entrypoint_object(logb)
add_math_entrypoint_object(logbf)
Expand Down
22 changes: 22 additions & 0 deletions libc/src/math/generic/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2289,6 +2289,28 @@ add_entrypoint_object(
-O3
)

add_entrypoint_object(
logf16
SRCS
logf16.cpp
HDRS
../logf16.h
DEPENDS
.expxf16
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.macros.optimization
libc.src.__support.macros.properties.cpu_features
COMPILE_OPTIONS
-O3
)

add_entrypoint_object(
logb
SRCS
Expand Down
28 changes: 28 additions & 0 deletions libc/src/math/generic/expxf16.h
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,34 @@ template <bool IsSinh> LIBC_INLINE float16 eval_sinh_or_cosh(float16 x) {
lo, half_p_odd * exp2_hi_mid_diff, half_p_even * exp2_hi_mid_sum));
}

// Generated by Sollya with the following commands:
// > display = hexadecimal;
// > for i from 0 to 31 do print(round(log(1 + i * 2^-5), SG, RN));
constexpr cpp::array<float, 32> LOGF_F = {
0x0p+0f, 0x1.f829bp-6f, 0x1.f0a30cp-5f, 0x1.6f0d28p-4f,
0x1.e27076p-4f, 0x1.29553p-3f, 0x1.5ff308p-3f, 0x1.9525aap-3f,
0x1.c8ff7cp-3f, 0x1.fb9186p-3f, 0x1.1675cap-2f, 0x1.2e8e2cp-2f,
0x1.4618bcp-2f, 0x1.5d1bdcp-2f, 0x1.739d8p-2f, 0x1.89a338p-2f,
0x1.9f323ep-2f, 0x1.b44f78p-2f, 0x1.c8ff7cp-2f, 0x1.dd46ap-2f,
0x1.f128f6p-2f, 0x1.02552ap-1f, 0x1.0be72ep-1f, 0x1.154c3ep-1f,
0x1.1e85f6p-1f, 0x1.2795e2p-1f, 0x1.307d74p-1f, 0x1.393e0ep-1f,
0x1.41d8fep-1f, 0x1.4a4f86p-1f, 0x1.52a2d2p-1f, 0x1.5ad404p-1f,
};

// Generated by Sollya with the following commands:
// > display = hexadecimal;
// > for i from 0 to 31 do print(round(1 / (1 + i * 2^-5), SG, RN));
constexpr cpp::array<float, 32> ONE_OVER_F_F = {
0x1p+0f, 0x1.f07c2p-1f, 0x1.e1e1e2p-1f, 0x1.d41d42p-1f,
0x1.c71c72p-1f, 0x1.bacf92p-1f, 0x1.af286cp-1f, 0x1.a41a42p-1f,
0x1.99999ap-1f, 0x1.8f9c18p-1f, 0x1.861862p-1f, 0x1.7d05f4p-1f,
0x1.745d18p-1f, 0x1.6c16c2p-1f, 0x1.642c86p-1f, 0x1.5c9882p-1f,
0x1.555556p-1f, 0x1.4e5e0ap-1f, 0x1.47ae14p-1f, 0x1.414142p-1f,
0x1.3b13b2p-1f, 0x1.3521dp-1f, 0x1.2f684cp-1f, 0x1.29e412p-1f,
0x1.24924ap-1f, 0x1.1f7048p-1f, 0x1.1a7b96p-1f, 0x1.15b1e6p-1f,
0x1.111112p-1f, 0x1.0c9714p-1f, 0x1.08421p-1f, 0x1.041042p-1f,
};

} // namespace LIBC_NAMESPACE_DECL

#endif // LLVM_LIBC_SRC_MATH_GENERIC_EXPXF16_H
157 changes: 157 additions & 0 deletions libc/src/math/generic/logf16.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
//===-- Half-precision log(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/logf16.h"
#include "expxf16.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/except_value_utils.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"
#include "src/__support/macros/properties/cpu_features.h"

namespace LIBC_NAMESPACE_DECL {

#ifdef LIBC_TARGET_CPU_HAS_FMA
static constexpr size_t N_LOGF16_EXCEPTS = 5;
#else
static constexpr size_t N_LOGF16_EXCEPTS = 11;
#endif

static constexpr fputil::ExceptValues<float16, N_LOGF16_EXCEPTS>
LOGF16_EXCEPTS = {{
// (input, RZ output, RU offset, RD offset, RN offset)
#ifndef LIBC_TARGET_CPU_HAS_FMA
// x = 0x1.61cp-13, logf16(x) = -0x1.16p+3 (RZ)
{0x0987U, 0xc858U, 0U, 1U, 0U},
// x = 0x1.f2p-12, logf16(x) = -0x1.e98p+2 (RZ)
{0x0fc8U, 0xc7a6U, 0U, 1U, 1U},
#endif
// x = 0x1.4d4p-9, logf16(x) = -0x1.7e4p+2 (RZ)
{0x1935U, 0xc5f9U, 0U, 1U, 0U},
// x = 0x1.5ep-8, logf16(x) = -0x1.4ecp+2 (RZ)
{0x1d78U, 0xc53bU, 0U, 1U, 0U},
#ifndef LIBC_TARGET_CPU_HAS_FMA
// x = 0x1.fdp-1, logf16(x) = -0x1.81p-8 (RZ)
{0x3bf4U, 0x9e04U, 0U, 1U, 1U},
// x = 0x1.fep-1, logf16(x) = -0x1.008p-8 (RZ)
{0x3bf8U, 0x9c02U, 0U, 1U, 0U},
#endif
// x = 0x1.ffp-1, logf16(x) = -0x1.004p-9 (RZ)
{0x3bfcU, 0x9801U, 0U, 1U, 0U},
// x = 0x1.ff8p-1, logf16(x) = -0x1p-10 (RZ)
{0x3bfeU, 0x9400U, 0U, 1U, 1U},
#ifdef LIBC_TARGET_CPU_HAS_FMA
// x = 0x1.4c4p+1, logf16(x) = 0x1.e84p-1 (RZ)
{0x4131U, 0x3ba1U, 1U, 0U, 1U},
#else
// x = 0x1.75p+2, logf16(x) = 0x1.c34p+0 (RZ)
{0x45d4U, 0x3f0dU, 1U, 0U, 0U},
// x = 0x1.75p+2, logf16(x) = 0x1.c34p+0 (RZ)
{0x45d4U, 0x3f0dU, 1U, 0U, 0U},
// x = 0x1.d5p+9, logf16(x) = 0x1.b5cp+2 (RZ)
{0x6354U, 0x46d7U, 1U, 0U, 1U},
#endif
}};

LLVM_LIBC_FUNCTION(float16, logf16, (float16 x)) {
using FPBits = fputil::FPBits<float16>;
FPBits x_bits(x);

uint16_t x_u = x_bits.uintval();

// If x <= 0, or x is 1, or x is +inf, or x is NaN.
if (LIBC_UNLIKELY(x_u == 0U || x_u == 0x3c00U || x_u >= 0x7c00U)) {
// log(NaN) = NaN
if (x_bits.is_nan()) {
if (x_bits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}

return x;
}

// log(+/-0) = −inf
if ((x_u & 0x7fffU) == 0U) {
fputil::raise_except_if_required(FE_DIVBYZERO);
return FPBits::inf(Sign::NEG).get_val();
}

if (x_u == 0x3c00U)
return FPBits::zero().get_val();

// When x < 0.
if (x_u > 0x8000U) {
fputil::set_errno_if_required(EDOM);
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}

// log(+inf) = +inf
return FPBits::inf().get_val();
}

if (auto r = LOGF16_EXCEPTS.lookup(x_u); LIBC_UNLIKELY(r.has_value()))
return r.value();

// To compute log(x), we perform the following range reduction:
// x = 2^m * 1.mant,
// log(x) = m * log(2) + log(1.mant).
// To compute log(1.mant), let f be the highest 6 bits including the hidden
// bit, and d be the difference (1.mant - f), i.e., the remaining 5 bits of
// the mantissa, then:
// log(1.mant) = log(f) + log(1.mant / f)
// = log(f) + log(1 + d/f)
// since d/f is sufficiently small.
// We store log(f) and 1/f in the lookup tables LOGF_F and ONE_OVER_F
// respectively.

int m = -FPBits::EXP_BIAS;

// When x is subnormal, normalize it.
if ((x_u & FPBits::EXP_MASK) == 0U) {
// Can't pass an integer to fputil::cast directly.
constexpr float NORMALIZE_EXP = 1U << FPBits::FRACTION_LEN;
x_bits = FPBits(x_bits.get_val() * fputil::cast<float16>(NORMALIZE_EXP));
x_u = x_bits.uintval();
m -= FPBits::FRACTION_LEN;
}

uint16_t mant = x_bits.get_mantissa();
// Leading 10 - 5 = 5 bits of the mantissa.
int f = mant >> 5;
// Unbiased exponent.
m += x_u >> FPBits::FRACTION_LEN;

// Set bits to 1.mant instead of 2^m * 1.mant.
x_bits.set_biased_exponent(FPBits::EXP_BIAS);
float mant_f = x_bits.get_val();
// v = 1.mant * 1/f - 1 = d/f
float v = fputil::multiply_add(mant_f, ONE_OVER_F_F[f], -1.0f);

// Degree-3 minimax polynomial generated by Sollya with the following
// commands:
// > display = hexadecimal;
// > P = fpminimax(log(1 + x)/x, 2, [|SG...|], [-2^-5, 2^-5]);
// > x * P;
float log1p_d_over_f =
v * fputil::polyeval(v, 0x1p+0f, -0x1.001804p-1f, 0x1.557ef6p-2f);
// log(1.mant) = log(f) + log(1 + d/f)
float log_1_mant = LOGF_F[f] + log1p_d_over_f;
return fputil::cast<float16>(
fputil::multiply_add(static_cast<float>(m), LOGF_2, log_1_mant));
}

} // namespace LIBC_NAMESPACE_DECL
21 changes: 21 additions & 0 deletions libc/src/math/logf16.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//===-- Implementation header for logf16 ------------------------*- 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_LOGF16_H
#define LLVM_LIBC_SRC_MATH_LOGF16_H

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

namespace LIBC_NAMESPACE_DECL {

float16 logf16(float16 x);

} // namespace LIBC_NAMESPACE_DECL

#endif // LLVM_LIBC_SRC_MATH_LOGF16_H
13 changes: 13 additions & 0 deletions libc/test/UnitTest/FPMatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -374,4 +374,17 @@ template <typename T> struct FPTest : public Test {
EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_MODE( \
(expected), (actual), (expected_except), RoundingMode::TowardZero)

#define EXPECT_FP_EQ_WITH_EXCEPTION_ALL_ROUNDING(expected, actual, \
expected_except) \
do { \
EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_NEAREST((expected), (actual), \
(expected_except)); \
EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_UPWARD((expected), (actual), \
(expected_except)); \
EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_DOWNWARD((expected), (actual), \
(expected_except)); \
EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_TOWARD_ZERO((expected), (actual), \
(expected_except)); \
} while (0)

#endif // LLVM_LIBC_TEST_UNITTEST_FPMATCHER_H
11 changes: 11 additions & 0 deletions libc/test/src/math/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1772,6 +1772,17 @@ add_fp_unittest(
libc.src.__support.FPUtil.fp_bits
)

add_fp_unittest(
logf16_test
NEED_MPFR
SUITE
libc-math-unittests
SRCS
logf16_test.cpp
DEPENDS
libc.src.math.logf16
)

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

using LlvmLibcLogf16Test = 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(LlvmLibcLogf16Test, PositiveRange) {
for (uint16_t v = POS_START; v <= POS_STOP; ++v) {
float16 x = FPBits(v).get_val();
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Log, x,
LIBC_NAMESPACE::logf16(x), 0.5);
}
}

TEST_F(LlvmLibcLogf16Test, NegativeRange) {
for (uint16_t v = NEG_START; v <= NEG_STOP; ++v) {
float16 x = FPBits(v).get_val();
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Log, x,
LIBC_NAMESPACE::logf16(x), 0.5);
}
}
13 changes: 13 additions & 0 deletions libc/test/src/math/smoke/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3558,6 +3558,19 @@ add_fp_unittest(
libc.src.__support.FPUtil.fp_bits
)

add_fp_unittest(
logf16_test
SUITE
libc-math-smoke-tests
SRCS
logf16_test.cpp
DEPENDS
libc.hdr.fenv_macros
libc.src.errno.errno
libc.src.math.logf16
libc.src.__support.FPUtil.cast
)

add_fp_unittest(
log2_test
SUITE
Expand Down
Loading
Loading