Skip to content

[libc][math][c23] Add {,u}fromfp{,x}{,f,l,f128} functions #86003

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 6 commits into from
Mar 25, 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
16 changes: 16 additions & 0 deletions libc/config/linux/x86_64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,12 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.frexp
libc.src.math.frexpf
libc.src.math.frexpl
libc.src.math.fromfp
libc.src.math.fromfpf
libc.src.math.fromfpl
libc.src.math.fromfpx
libc.src.math.fromfpxf
libc.src.math.fromfpxl
libc.src.math.hypot
libc.src.math.hypotf
libc.src.math.ilogb
Expand Down Expand Up @@ -471,6 +477,12 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.trunc
libc.src.math.truncf
libc.src.math.truncl
libc.src.math.ufromfp
libc.src.math.ufromfpf
libc.src.math.ufromfpl
libc.src.math.ufromfpx
libc.src.math.ufromfpxf
libc.src.math.ufromfpxl
)

if(LIBC_TYPES_HAS_FLOAT128)
Expand All @@ -485,6 +497,8 @@ if(LIBC_TYPES_HAS_FLOAT128)
libc.src.math.fminf128
libc.src.math.fmodf128
libc.src.math.frexpf128
libc.src.math.fromfpf128
libc.src.math.fromfpxf128
libc.src.math.ilogbf128
libc.src.math.ldexpf128
libc.src.math.llogbf128
Expand All @@ -502,6 +516,8 @@ if(LIBC_TYPES_HAS_FLOAT128)
libc.src.math.roundf128
libc.src.math.sqrtf128
libc.src.math.truncf128
libc.src.math.ufromfpf128
libc.src.math.ufromfpxf128
)
endif()

Expand Down
8 changes: 8 additions & 0 deletions libc/docs/dev/undefined_behavior.rst
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,11 @@ The C standard does not specify behavior for ``printf("%s", NULL)``. We will
print the string literal ``(null)`` unless using the
``LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS`` option described in :ref:`printf
behavior<printf_behavior>`.

Unknown Math Rounding Direction
-------------------------------
The C23 standard states that if the value of the ``rnd`` argument of the
``fromfp``, ``ufromfp``, ``fromfpx`` and ``ufromfpx`` functions is not equal to
the value of a math rounding direction macro, the direction of rounding is
unspecified. LLVM's libc chooses to use the ``FP_INT_TONEAREST`` rounding
direction in this case.
32 changes: 32 additions & 0 deletions libc/docs/math/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,22 @@ Basic Operations
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| frexpf128 | |check| | |check| | | |check| | | | | | | | | |
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| fromfp | |check| | | | | | | | | | | | |
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| fromfpf | |check| | | | | | | | | | | | |
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| fromfpl | |check| | | | | | | | | | | | |
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| fromfpf128 | |check| | | | | | | | | | | | |
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| fromfpx | |check| | | | | | | | | | | | |
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| fromfpxf | |check| | | | | | | | | | | | |
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| fromfpxl | |check| | | | | | | | | | | | |
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| fromfpxf128 | |check| | | | | | | | | | | | |
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| ilogb | |check| | |check| | |check| | |check| | |check| | | | |check| | |check| | |check| | | |
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| ilogbf | |check| | |check| | |check| | |check| | |check| | | | |check| | |check| | |check| | | |
Expand Down Expand Up @@ -339,6 +355,22 @@ Basic Operations
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| truncf128 | |check| | |check| | | |check| | | | | | | | | |
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| ufromfp | |check| | | | | | | | | | | | |
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| ufromfpf | |check| | | | | | | | | | | | |
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| ufromfpl | |check| | | | | | | | | | | | |
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| ufromfpf128 | |check| | | | | | | | | | | | |
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| ufromfpx | |check| | | | | | | | | | | | |
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| ufromfpxf | |check| | | | | | | | | | | | |
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| ufromfpxl | |check| | | | | | | | | | | | |
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| ufromfpxf128 | |check| | | | | | | | | | | | |
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+


Higher Math Functions
Expand Down
6 changes: 6 additions & 0 deletions libc/include/llvm-libc-macros/math-macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@
#define FP_SUBNORMAL 3
#define FP_NORMAL 4

#define FP_INT_UPWARD 0
#define FP_INT_DOWNWARD 1
#define FP_INT_TOWARDZERO 2
#define FP_INT_TONEARESTFROMZERO 3
#define FP_INT_TONEAREST 4

#define MATH_ERRNO 1
#define MATH_ERREXCEPT 2

Expand Down
26 changes: 26 additions & 0 deletions libc/spec/stdc.td
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,12 @@ def StdC : StandardSpec<"stdc"> {
Macro<"INFINITY">,
Macro<"NAN">,

Macro<"FP_INT_UPWARD">,
Macro<"FP_INT_DOWNWARD">,
Macro<"FP_INT_TOWARDZERO">,
Macro<"FP_INT_TONEARESTFROMZERO">,
Macro<"FP_INT_TONEAREST">,

Macro<"FP_ILOGB0">,
Macro<"FP_ILOGBNAN">,

Expand Down Expand Up @@ -414,6 +420,26 @@ def StdC : StandardSpec<"stdc"> {
FunctionSpec<"frexpl", RetValSpec<LongDoubleType>, [ArgSpec<LongDoubleType>, ArgSpec<IntPtr>]>,
GuardedFunctionSpec<"frexpf128", RetValSpec<Float128Type>, [ArgSpec<Float128Type>, ArgSpec<IntPtr>], "LIBC_TYPES_HAS_FLOAT128">,

FunctionSpec<"fromfp", RetValSpec<DoubleType>, [ArgSpec<DoubleType>, ArgSpec<IntType>, ArgSpec<UnsignedIntType>]>,
FunctionSpec<"fromfpf", RetValSpec<FloatType>, [ArgSpec<FloatType>, ArgSpec<IntType>, ArgSpec<UnsignedIntType>]>,
FunctionSpec<"fromfpl", RetValSpec<LongDoubleType>, [ArgSpec<LongDoubleType>, ArgSpec<IntType>, ArgSpec<UnsignedIntType>]>,
GuardedFunctionSpec<"fromfpf128", RetValSpec<LongDoubleType>, [ArgSpec<LongDoubleType>, ArgSpec<IntType>, ArgSpec<UnsignedIntType>], "LIBC_TYPES_HAS_FLOAT128">,

FunctionSpec<"fromfpx", RetValSpec<DoubleType>, [ArgSpec<DoubleType>, ArgSpec<IntType>, ArgSpec<UnsignedIntType>]>,
FunctionSpec<"fromfpxf", RetValSpec<FloatType>, [ArgSpec<FloatType>, ArgSpec<IntType>, ArgSpec<UnsignedIntType>]>,
FunctionSpec<"fromfpxl", RetValSpec<LongDoubleType>, [ArgSpec<LongDoubleType>, ArgSpec<IntType>, ArgSpec<UnsignedIntType>]>,
GuardedFunctionSpec<"fromfpxf128", RetValSpec<LongDoubleType>, [ArgSpec<LongDoubleType>, ArgSpec<IntType>, ArgSpec<UnsignedIntType>], "LIBC_TYPES_HAS_FLOAT128">,

FunctionSpec<"ufromfp", RetValSpec<DoubleType>, [ArgSpec<DoubleType>, ArgSpec<IntType>, ArgSpec<UnsignedIntType>]>,
FunctionSpec<"ufromfpf", RetValSpec<FloatType>, [ArgSpec<FloatType>, ArgSpec<IntType>, ArgSpec<UnsignedIntType>]>,
FunctionSpec<"ufromfpl", RetValSpec<LongDoubleType>, [ArgSpec<LongDoubleType>, ArgSpec<IntType>, ArgSpec<UnsignedIntType>]>,
GuardedFunctionSpec<"ufromfpf128", RetValSpec<LongDoubleType>, [ArgSpec<LongDoubleType>, ArgSpec<IntType>, ArgSpec<UnsignedIntType>], "LIBC_TYPES_HAS_FLOAT128">,

FunctionSpec<"ufromfpx", RetValSpec<DoubleType>, [ArgSpec<DoubleType>, ArgSpec<IntType>, ArgSpec<UnsignedIntType>]>,
FunctionSpec<"ufromfpxf", RetValSpec<FloatType>, [ArgSpec<FloatType>, ArgSpec<IntType>, ArgSpec<UnsignedIntType>]>,
FunctionSpec<"ufromfpxl", RetValSpec<LongDoubleType>, [ArgSpec<LongDoubleType>, ArgSpec<IntType>, ArgSpec<UnsignedIntType>]>,
GuardedFunctionSpec<"ufromfpxf128", RetValSpec<LongDoubleType>, [ArgSpec<LongDoubleType>, ArgSpec<IntType>, ArgSpec<UnsignedIntType>], "LIBC_TYPES_HAS_FLOAT128">,

FunctionSpec<"hypot", RetValSpec<DoubleType>, [ArgSpec<DoubleType>, ArgSpec<DoubleType>]>,
FunctionSpec<"hypotf", RetValSpec<FloatType>, [ArgSpec<FloatType>, ArgSpec<FloatType>]>,

Expand Down
94 changes: 79 additions & 15 deletions libc/src/__support/FPUtil/NearestIntegerOperations.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,9 @@ LIBC_INLINE T round(T x) {
}
}

template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
LIBC_INLINE T round_using_current_rounding_mode(T x) {
template <typename T>
LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_floating_point_v<T>, T>
round_using_specific_rounding_mode(T x, int rnd) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this function should have been made static or put in a namespace.

using StorageType = typename FPBits<T>::StorageType;
FPBits<T> bits(x);

Expand All @@ -151,28 +152,30 @@ LIBC_INLINE T round_using_current_rounding_mode(T x) {

bool is_neg = bits.is_neg();
int exponent = bits.get_exponent();
int rounding_mode = quick_get_round();

// If the exponent is greater than the most negative mantissa
// exponent, then x is already an integer.
if (exponent >= static_cast<int>(FPBits<T>::FRACTION_LEN))
return x;

if (exponent <= -1) {
switch (rounding_mode) {
case FE_DOWNWARD:
switch (rnd) {
case FP_INT_DOWNWARD:
return is_neg ? T(-1.0) : T(0.0);
case FE_UPWARD:
case FP_INT_UPWARD:
return is_neg ? T(-0.0) : T(1.0);
case FE_TOWARDZERO:
case FP_INT_TOWARDZERO:
return is_neg ? T(-0.0) : T(0.0);
case FE_TONEAREST:
case FP_INT_TONEARESTFROMZERO:
if (exponent < -1)
return is_neg ? T(-0.0) : T(0.0); // abs(x) < 0.5
return is_neg ? T(-1.0) : T(1.0); // abs(x) >= 0.5
case FP_INT_TONEAREST:
default:
if (exponent <= -2 || bits.get_mantissa() == 0)
return is_neg ? T(-0.0) : T(0.0); // abs(x) <= 0.5
else
return is_neg ? T(-1.0) : T(1.0); // abs(x) > 0.5
default:
__builtin_unreachable();
}
}

Expand All @@ -194,14 +197,19 @@ LIBC_INLINE T round_using_current_rounding_mode(T x) {
StorageType trunc_is_odd =
new_bits.get_mantissa() & (StorageType(1) << trim_size);

switch (rounding_mode) {
case FE_DOWNWARD:
switch (rnd) {
case FP_INT_DOWNWARD:
return is_neg ? trunc_value - T(1.0) : trunc_value;
case FE_UPWARD:
case FP_INT_UPWARD:
return is_neg ? trunc_value : trunc_value + T(1.0);
case FE_TOWARDZERO:
case FP_INT_TOWARDZERO:
return trunc_value;
case FE_TONEAREST:
case FP_INT_TONEARESTFROMZERO:
if (trim_value >= half_value)
return is_neg ? trunc_value - T(1.0) : trunc_value + T(1.0);
return trunc_value;
case FP_INT_TONEAREST:
default:
if (trim_value > half_value) {
return is_neg ? trunc_value - T(1.0) : trunc_value + T(1.0);
} else if (trim_value == half_value) {
Expand All @@ -214,11 +222,67 @@ LIBC_INLINE T round_using_current_rounding_mode(T x) {
} else {
return trunc_value;
}
}
}

template <typename T>
LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, T>
round_using_current_rounding_mode(T x) {
int rounding_mode = quick_get_round();

switch (rounding_mode) {
case FE_DOWNWARD:
return round_using_specific_rounding_mode(x, FP_INT_DOWNWARD);
case FE_UPWARD:
return round_using_specific_rounding_mode(x, FP_INT_UPWARD);
case FE_TOWARDZERO:
return round_using_specific_rounding_mode(x, FP_INT_TOWARDZERO);
case FE_TONEAREST:
return round_using_specific_rounding_mode(x, FP_INT_TONEAREST);
default:
__builtin_unreachable();
}
}

template <bool IsSigned, typename T>
LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_floating_point_v<T>, T>
fromfp(T x, int rnd, unsigned int width) {
if (width == 0U)
return FPBits<T>::quiet_nan().get_val();

T rounded_value = round_using_specific_rounding_mode(x, rnd);

if constexpr (IsSigned) {
// T can't hold a finite number >= 2.0 * 2^EXP_BIAS.
if (width - 1 > FPBits<T>::EXP_BIAS)
return rounded_value;
if (rounded_value < -T(1U << (width - 1U)))
return FPBits<T>::quiet_nan().get_val();
if (rounded_value > T((1U << (width - 1U)) - 1U))
return FPBits<T>::quiet_nan().get_val();
return rounded_value;
}

if (rounded_value < T(0.0))
return FPBits<T>::quiet_nan().get_val();
// T can't hold a finite number >= 2.0 * 2^EXP_BIAS.
if (width <= FPBits<T>::EXP_BIAS && rounded_value > T(1U << width) - 1U)
return FPBits<T>::quiet_nan().get_val();
return rounded_value;
}

template <bool IsSigned, typename T>
LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_floating_point_v<T>, T>
fromfpx(T x, int rnd, unsigned int width) {
T rounded_value = fromfp<IsSigned>(x, rnd, width);
FPBits<T> bits(rounded_value);

if (!bits.is_nan() && rounded_value != x)
raise_except_if_required(FE_INEXACT);

return rounded_value;
}

namespace internal {

template <typename F, typename I,
Expand Down
20 changes: 20 additions & 0 deletions libc/src/math/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,16 @@ add_math_entrypoint_object(frexpf)
add_math_entrypoint_object(frexpl)
add_math_entrypoint_object(frexpf128)

add_math_entrypoint_object(fromfp)
add_math_entrypoint_object(fromfpf)
add_math_entrypoint_object(fromfpl)
add_math_entrypoint_object(fromfpf128)

add_math_entrypoint_object(fromfpx)
add_math_entrypoint_object(fromfpxf)
add_math_entrypoint_object(fromfpxl)
add_math_entrypoint_object(fromfpxf128)

add_math_entrypoint_object(hypot)
add_math_entrypoint_object(hypotf)

Expand Down Expand Up @@ -267,3 +277,13 @@ add_math_entrypoint_object(trunc)
add_math_entrypoint_object(truncf)
add_math_entrypoint_object(truncl)
add_math_entrypoint_object(truncf128)

add_math_entrypoint_object(ufromfp)
add_math_entrypoint_object(ufromfpf)
add_math_entrypoint_object(ufromfpl)
add_math_entrypoint_object(ufromfpf128)

add_math_entrypoint_object(ufromfpx)
add_math_entrypoint_object(ufromfpxf)
add_math_entrypoint_object(ufromfpxl)
add_math_entrypoint_object(ufromfpxf128)
18 changes: 18 additions & 0 deletions libc/src/math/fromfp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//===-- Implementation header for fromfp ------------------------*- 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_FROMFP_H
#define LLVM_LIBC_SRC_MATH_FROMFP_H

namespace LIBC_NAMESPACE {

double fromfp(double x, int rnd, unsigned int width);

} // namespace LIBC_NAMESPACE

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

namespace LIBC_NAMESPACE {

float fromfpf(float x, int rnd, unsigned int width);

} // namespace LIBC_NAMESPACE

#endif // LLVM_LIBC_SRC_MATH_FROMFPF_H
20 changes: 20 additions & 0 deletions libc/src/math/fromfpf128.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===-- Implementation header for fromfpf128 --------------------*- 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_FROMFPF128_H
#define LLVM_LIBC_SRC_MATH_FROMFPF128_H

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

namespace LIBC_NAMESPACE {

float128 fromfpf128(float128 x, int rnd, unsigned int width);

} // namespace LIBC_NAMESPACE

#endif // LLVM_LIBC_SRC_MATH_FROMFPF128_H
Loading