Skip to content

[libc][math][c23] Add fmodl and fmodf128 math functions. #84600

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 4 commits into from
Mar 11, 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
2 changes: 2 additions & 0 deletions libc/config/linux/aarch64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.fminl
libc.src.math.fmod
libc.src.math.fmodf
libc.src.math.fmodl
libc.src.math.frexp
libc.src.math.frexpf
libc.src.math.frexpl
Expand Down Expand Up @@ -426,6 +427,7 @@ if(LIBC_TYPES_HAS_FLOAT128)
libc.src.math.floorf128
libc.src.math.fmaxf128
libc.src.math.fminf128
libc.src.math.fmodf128
libc.src.math.frexpf128
libc.src.math.ilogbf128
libc.src.math.ldexpf128
Expand Down
2 changes: 2 additions & 0 deletions libc/config/linux/riscv/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.fmaxl
libc.src.math.fmod
libc.src.math.fmodf
libc.src.math.fmodl
libc.src.math.frexp
libc.src.math.frexpf
libc.src.math.frexpl
Expand Down Expand Up @@ -434,6 +435,7 @@ if(LIBC_TYPES_HAS_FLOAT128)
libc.src.math.floorf128
libc.src.math.fmaxf128
libc.src.math.fminf128
libc.src.math.fmodf128
libc.src.math.frexpf128
libc.src.math.ilogbf128
libc.src.math.ldexpf128
Expand Down
2 changes: 2 additions & 0 deletions libc/config/linux/x86_64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.fmaxl
libc.src.math.fmod
libc.src.math.fmodf
libc.src.math.fmodl
libc.src.math.frexp
libc.src.math.frexpf
libc.src.math.frexpl
Expand Down Expand Up @@ -469,6 +470,7 @@ if(LIBC_TYPES_HAS_FLOAT128)
libc.src.math.floorf128
libc.src.math.fmaxf128
libc.src.math.fminf128
libc.src.math.fmodf128
libc.src.math.frexpf128
libc.src.math.ilogbf128
libc.src.math.ldexpf128
Expand Down
1 change: 1 addition & 0 deletions libc/config/windows/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.fmaxl
libc.src.math.fmod
libc.src.math.fmodf
libc.src.math.fmodl
libc.src.math.frexp
libc.src.math.frexpf
libc.src.math.frexpl
Expand Down
4 changes: 3 additions & 1 deletion libc/docs/math/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,9 @@ Basic Operations
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| fmodf | |check| | |check| | |check| | |check| | |check| | | | |check| | |check| | |check| | | |
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| fmodl | | | | | | | | | | | | |
| fmodl | |check| | |check| | | |check| | |check| | | | |check| | | | | |
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| fmodf128 | |check| | |check| | | |check| | | | | | | | | |
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| frexp | |check| | |check| | |check| | |check| | |check| | | | |check| | |check| | |check| | | |
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
Expand Down
3 changes: 2 additions & 1 deletion libc/spec/stdc.td
Original file line number Diff line number Diff line change
Expand Up @@ -405,8 +405,9 @@ def StdC : StandardSpec<"stdc"> {
FunctionSpec<"fmaf", RetValSpec<FloatType>, [ArgSpec<FloatType>, ArgSpec<FloatType>, ArgSpec<FloatType>]>,

FunctionSpec<"fmod", RetValSpec<DoubleType>, [ArgSpec<DoubleType>, ArgSpec<DoubleType>]>,

FunctionSpec<"fmodf", RetValSpec<FloatType>, [ArgSpec<FloatType>, ArgSpec<FloatType>]>,
FunctionSpec<"fmodl", RetValSpec<LongDoubleType>, [ArgSpec<LongDoubleType>, ArgSpec<LongDoubleType>]>,
GuardedFunctionSpec<"fmodf128", RetValSpec<Float128Type>, [ArgSpec<Float128Type>, ArgSpec<Float128Type>], "LIBC_TYPES_HAS_FLOAT128">,

FunctionSpec<"frexp", RetValSpec<DoubleType>, [ArgSpec<DoubleType>, ArgSpec<IntPtr>]>,
FunctionSpec<"frexpf", RetValSpec<FloatType>, [ArgSpec<FloatType>, ArgSpec<IntPtr>]>,
Expand Down
17 changes: 10 additions & 7 deletions libc/src/__support/FPUtil/FPBits.h
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,7 @@ struct FPRepImpl : public FPRepSem<fp_type, RetT> {
using UP::EXP_MASK;
using UP::FRACTION_MASK;
using UP::SIG_LEN;
using UP::SIG_MASK;
using UP::SIGN_MASK;
LIBC_INLINE_VAR static constexpr int MAX_BIASED_EXPONENT =
(1 << UP::EXP_LEN) - 1;
Expand Down Expand Up @@ -729,6 +730,9 @@ struct FPRepImpl : public FPRepSem<fp_type, RetT> {
bits = UP::merge(bits, mantVal, FRACTION_MASK);
}

LIBC_INLINE constexpr void set_significand(StorageType sigVal) {
bits = UP::merge(bits, sigVal, SIG_MASK);
}
// Unsafe function to create a floating point representation.
// It simply packs the sign, biased exponent and mantissa values without
// checking bound nor normalization.
Expand All @@ -755,20 +759,19 @@ struct FPRepImpl : public FPRepSem<fp_type, RetT> {
// 4) "number" zero value is not processed correctly.
// 5) Number is unsigned, so the result can be only positive.
LIBC_INLINE static constexpr RetT make_value(StorageType number, int ep) {
static_assert(fp_type != FPType::X86_Binary80,
"This function is not tested for X86 Extended Precision");
FPRepImpl result;
// offset: +1 for sign, but -1 for implicit first bit
int lz = cpp::countl_zero(number) - UP::EXP_LEN;
FPRepImpl result(0);
int lz =
UP::FRACTION_LEN + 1 - (UP::STORAGE_LEN - cpp::countl_zero(number));

number <<= lz;
ep -= lz;

if (LIBC_LIKELY(ep >= 0)) {
// Implicit number bit will be removed by mask
result.set_mantissa(number);
result.set_significand(number);
result.set_biased_exponent(ep + 1);
} else {
result.set_mantissa(number >> -ep);
result.set_significand(number >> -ep);
}
return RetT(result.uintval());
}
Expand Down
143 changes: 56 additions & 87 deletions libc/src/__support/FPUtil/generic/FMod.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,63 +117,9 @@ namespace generic {
// be implemented in another handler.
// Signaling NaN converted to quiet NaN with FE_INVALID exception.
// https://www.open-std.org/JTC1/SC22/WG14/www/docs/n1011.htm
template <typename T> struct FModExceptionalInputHandler {

static_assert(cpp::is_floating_point_v<T>,
"FModCStandardWrapper instantiated with invalid type.");

LIBC_INLINE static bool pre_check(T x, T y, T &out) {
using FPB = fputil::FPBits<T>;
const T quiet_nan = FPB::quiet_nan().get_val();
FPB sx(x), sy(y);
if (LIBC_LIKELY(!sy.is_zero() && !sy.is_inf_or_nan() &&
!sx.is_inf_or_nan())) {
return false;
}

if (sx.is_nan() || sy.is_nan()) {
if ((sx.is_nan() && !sx.is_quiet_nan()) ||
(sy.is_nan() && !sy.is_quiet_nan()))
fputil::raise_except_if_required(FE_INVALID);
out = quiet_nan;
return true;
}

if (sx.is_inf() || sy.is_zero()) {
fputil::raise_except_if_required(FE_INVALID);
fputil::set_errno_if_required(EDOM);
out = quiet_nan;
return true;
}

if (sy.is_inf()) {
out = x;
return true;
}

// case where x == 0
out = x;
return true;
}
};

template <typename T> struct FModFastMathWrapper {

static_assert(cpp::is_floating_point_v<T>,
"FModFastMathWrapper instantiated with invalid type.");

static bool pre_check(T, T, T &) { return false; }
};

template <typename T> class FModDivisionSimpleHelper {
private:
using StorageType = typename FPBits<T>::StorageType;

public:
LIBC_INLINE constexpr static StorageType execute(int exp_diff,
int sides_zeroes_count,
StorageType m_x,
StorageType m_y) {
template <typename T> struct FModDivisionSimpleHelper {
LIBC_INLINE constexpr static T execute(int exp_diff, int sides_zeroes_count,
T m_x, T m_y) {
while (exp_diff > sides_zeroes_count) {
exp_diff -= sides_zeroes_count;
m_x <<= sides_zeroes_count;
Expand All @@ -185,28 +131,21 @@ template <typename T> class FModDivisionSimpleHelper {
}
};

template <typename T> class FModDivisionInvMultHelper {
private:
using FPB = FPBits<T>;
using StorageType = typename FPB::StorageType;

public:
LIBC_INLINE constexpr static StorageType execute(int exp_diff,
int sides_zeroes_count,
StorageType m_x,
StorageType m_y) {
template <typename T> struct FModDivisionInvMultHelper {
LIBC_INLINE constexpr static T execute(int exp_diff, int sides_zeroes_count,
T m_x, T m_y) {
constexpr int LENGTH = sizeof(T) * CHAR_BIT;
if (exp_diff > sides_zeroes_count) {
StorageType inv_hy = (cpp::numeric_limits<StorageType>::max() / m_y);
T inv_hy = (cpp::numeric_limits<T>::max() / m_y);
while (exp_diff > sides_zeroes_count) {
exp_diff -= sides_zeroes_count;
StorageType hd =
(m_x * inv_hy) >> (FPB::TOTAL_LEN - sides_zeroes_count);
T hd = (m_x * inv_hy) >> (LENGTH - sides_zeroes_count);
m_x <<= sides_zeroes_count;
m_x -= hd * m_y;
while (LIBC_UNLIKELY(m_x > m_y))
m_x -= m_y;
}
StorageType hd = (m_x * inv_hy) >> (FPB::TOTAL_LEN - exp_diff);
T hd = (m_x * inv_hy) >> (LENGTH - exp_diff);
m_x <<= exp_diff;
m_x -= hd * m_y;
while (LIBC_UNLIKELY(m_x > m_y))
Expand All @@ -219,22 +158,49 @@ template <typename T> class FModDivisionInvMultHelper {
}
};

template <typename T, class Wrapper = FModExceptionalInputHandler<T>,
class DivisionHelper = FModDivisionSimpleHelper<T>>
template <typename T, typename U = typename FPBits<T>::StorageType,
typename DivisionHelper = FModDivisionSimpleHelper<U>>
class FMod {
static_assert(cpp::is_floating_point_v<T>,
static_assert(cpp::is_floating_point_v<T> && cpp::is_unsigned_v<U> &&
(sizeof(U) * CHAR_BIT > FPBits<T>::FRACTION_LEN),
"FMod instantiated with invalid type.");

private:
using FPB = FPBits<T>;
using StorageType = typename FPB::StorageType;

LIBC_INLINE static bool pre_check(T x, T y, T &out) {
using FPB = fputil::FPBits<T>;
const T quiet_nan = FPB::quiet_nan().get_val();
FPB sx(x), sy(y);
if (LIBC_LIKELY(!sy.is_zero() && !sy.is_inf_or_nan() &&
!sx.is_inf_or_nan()))
return false;

if (sx.is_nan() || sy.is_nan()) {
if (sx.is_signaling_nan() || sy.is_signaling_nan())
fputil::raise_except_if_required(FE_INVALID);
out = quiet_nan;
return true;
}

if (sx.is_inf() || sy.is_zero()) {
fputil::raise_except_if_required(FE_INVALID);
fputil::set_errno_if_required(EDOM);
out = quiet_nan;
return true;
}

out = x;
return true;
}

LIBC_INLINE static constexpr FPB eval_internal(FPB sx, FPB sy) {

if (LIBC_LIKELY(sx.uintval() <= sy.uintval())) {
if (sx.uintval() < sy.uintval())
return sx; // |x|<|y| return x
return FPB(FPB::zero()); // |x|=|y| return 0.0
return FPB::zero(); // |x|=|y| return 0.0
}

int e_x = sx.get_biased_exponent();
Expand All @@ -247,27 +213,29 @@ class FMod {
StorageType m_y = sy.get_explicit_mantissa();
StorageType d = (e_x == e_y) ? (m_x - m_y) : (m_x << (e_x - e_y)) % m_y;
if (d == 0)
return FPB(FPB::zero());
return FPB::zero();
// iy - 1 because of "zero power" for number with power 1
return FPB::make_value(d, e_y - 1);
}
/* Both subnormal special case. */
// Both subnormal special case.
if (LIBC_UNLIKELY(e_x == 0 && e_y == 0)) {
FPB d;
d.set_mantissa(sx.uintval() % sy.uintval());
return d;
}

// Note that hx is not subnormal by conditions above.
StorageType m_x = sx.get_explicit_mantissa();
U m_x = static_cast<U>(sx.get_explicit_mantissa());
e_x--;

StorageType m_y = sy.get_explicit_mantissa();
int lead_zeros_m_y = FPB::EXP_LEN;
U m_y = static_cast<U>(sy.get_explicit_mantissa());
constexpr int DEFAULT_LEAD_ZEROS =
sizeof(U) * CHAR_BIT - FPB::FRACTION_LEN - 1;
int lead_zeros_m_y = DEFAULT_LEAD_ZEROS;
if (LIBC_LIKELY(e_y > 0)) {
e_y--;
} else {
m_y = sy.get_mantissa();
m_y = static_cast<U>(sy.get_mantissa());
lead_zeros_m_y = cpp::countl_zero(m_y);
}

Expand All @@ -286,26 +254,27 @@ class FMod {

{
// Shift hx left until the end or n = 0
int left_shift = exp_diff < int(FPB::EXP_LEN) ? exp_diff : FPB::EXP_LEN;
int left_shift =
exp_diff < DEFAULT_LEAD_ZEROS ? exp_diff : DEFAULT_LEAD_ZEROS;
m_x <<= left_shift;
exp_diff -= left_shift;
}

m_x %= m_y;
if (LIBC_UNLIKELY(m_x == 0))
return FPB(FPB::zero());
return FPB::zero();

if (exp_diff == 0)
return FPB::make_value(m_x, e_y);
return FPB::make_value(static_cast<StorageType>(m_x), e_y);

/* hx next can't be 0, because hx < hy, hy % 2 == 1 hx * 2^i % hy != 0 */
// hx next can't be 0, because hx < hy, hy % 2 == 1 hx * 2^i % hy != 0
m_x = DivisionHelper::execute(exp_diff, sides_zeroes_count, m_x, m_y);
return FPB::make_value(m_x, e_y);
return FPB::make_value(static_cast<StorageType>(m_x), e_y);
}

public:
LIBC_INLINE static T eval(T x, T y) {
if (T out; Wrapper::pre_check(x, y, out))
if (T out; LIBC_UNLIKELY(pre_check(x, y, out)))
return out;
FPB sx(x), sy(y);
Sign sign = sx.sign();
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 @@ -119,6 +119,8 @@ add_math_entrypoint_object(fminf128)

add_math_entrypoint_object(fmod)
add_math_entrypoint_object(fmodf)
add_math_entrypoint_object(fmodl)
add_math_entrypoint_object(fmodf128)

add_math_entrypoint_object(frexp)
add_math_entrypoint_object(frexpf)
Expand Down
20 changes: 20 additions & 0 deletions libc/src/math/fmodf128.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===-- Implementation header for fmodf128 ----------------------*- 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_FMODF128_H
#define LLVM_LIBC_SRC_MATH_FMODF128_H

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

namespace LIBC_NAMESPACE {

float128 fmodf128(float128 x, float128 y);

} // namespace LIBC_NAMESPACE

#endif // LLVM_LIBC_SRC_MATH_FMODF128_H
18 changes: 18 additions & 0 deletions libc/src/math/fmodl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//===-- Implementation header for fmodl -------------------------*- 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_FMODL_H
#define LLVM_LIBC_SRC_MATH_FMODL_H

namespace LIBC_NAMESPACE {

long double fmodl(long double x, long double y);

} // namespace LIBC_NAMESPACE

#endif // LLVM_LIBC_SRC_MATH_FMODL_H
Loading