Skip to content

Commit b43965a

Browse files
author
Michael Flanders
authored
[libc][math][c23] adds nanf128 (#85201)
Continuing #84689, this one required more changes than the others, so I am making it a separate PR. Extends some stuff in `str_to_float.h`, `str_to_integer.h` to work on types wider than `unsigned long long` and `uint64_t`. cc @lntue for review.
1 parent 2210c85 commit b43965a

File tree

15 files changed

+235
-42
lines changed

15 files changed

+235
-42
lines changed

libc/config/linux/aarch64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,7 @@ if(LIBC_TYPES_HAS_FLOAT128)
468468
libc.src.math.lrintf128
469469
libc.src.math.lroundf128
470470
libc.src.math.modff128
471+
libc.src.math.nanf128
471472
libc.src.math.nextafterf128
472473
libc.src.math.rintf128
473474
libc.src.math.roundf128

libc/config/linux/riscv/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,7 @@ if(LIBC_TYPES_HAS_FLOAT128)
476476
libc.src.math.lrintf128
477477
libc.src.math.lroundf128
478478
libc.src.math.modff128
479+
libc.src.math.nanf128
479480
libc.src.math.nextafterf128
480481
libc.src.math.rintf128
481482
libc.src.math.roundf128

libc/config/linux/x86_64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,7 @@ if(LIBC_TYPES_HAS_FLOAT128)
481481
libc.src.math.lrintf128
482482
libc.src.math.lroundf128
483483
libc.src.math.modff128
484+
libc.src.math.nanf128
484485
libc.src.math.nextafterf128
485486
libc.src.math.rintf128
486487
libc.src.math.roundf128

libc/docs/math/index.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,8 @@ Basic Operations
259259
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
260260
| nanl | |check| | |check| | |check| | |check| | |check| | | | |check| | |check| | |check| | | |
261261
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
262+
| nanf128 | |check| | |check| | | |check| | | | | | | | | |
263+
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
262264
| nearbyint | |check| | |check| | |check| | |check| | |check| | | | |check| | |check| | |check| | | |
263265
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
264266
| nearbyintf | |check| | |check| | |check| | |check| | |check| | | | |check| | |check| | |check| | | |

libc/spec/stdc.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,7 @@ def StdC : StandardSpec<"stdc"> {
559559
FunctionSpec<"nanf", RetValSpec<FloatType>, [ArgSpec<ConstCharPtr>]>,
560560
FunctionSpec<"nan", RetValSpec<DoubleType>, [ArgSpec<ConstCharPtr>]>,
561561
FunctionSpec<"nanl", RetValSpec<LongDoubleType>, [ArgSpec<ConstCharPtr>]>,
562+
GuardedFunctionSpec<"nanf128", RetValSpec<Float128Type>, [ArgSpec<ConstCharPtr>], "LIBC_TYPES_HAS_FLOAT128">,
562563
]
563564
>;
564565

libc/src/__support/UInt.h

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -993,6 +993,68 @@ struct is_big_int<BigInt<Bits, Signed, T>> : cpp::true_type {};
993993
template <class T>
994994
LIBC_INLINE_VAR constexpr bool is_big_int_v = is_big_int<T>::value;
995995

996+
// extensions of type traits to include BigInt
997+
998+
// is_integral_or_big_int
999+
template <typename T>
1000+
struct is_integral_or_big_int
1001+
: cpp::bool_constant<(cpp::is_integral_v<T> || is_big_int_v<T>)> {};
1002+
1003+
template <typename T>
1004+
LIBC_INLINE_VAR constexpr bool is_integral_or_big_int_v =
1005+
is_integral_or_big_int<T>::value;
1006+
1007+
// make_big_int_unsigned
1008+
template <typename T> struct make_big_int_unsigned;
1009+
1010+
template <size_t Bits, bool Signed, typename T>
1011+
struct make_big_int_unsigned<BigInt<Bits, Signed, T>>
1012+
: cpp::type_identity<BigInt<Bits, false, T>> {};
1013+
1014+
template <typename T>
1015+
using make_big_int_unsigned_t = typename make_big_int_unsigned<T>::type;
1016+
1017+
// make_big_int_signed
1018+
template <typename T> struct make_big_int_signed;
1019+
1020+
template <size_t Bits, bool Signed, typename T>
1021+
struct make_big_int_signed<BigInt<Bits, Signed, T>>
1022+
: cpp::type_identity<BigInt<Bits, true, T>> {};
1023+
1024+
template <typename T>
1025+
using make_big_int_signed_t = typename make_big_int_signed<T>::type;
1026+
1027+
// make_integral_or_big_int_unsigned
1028+
template <typename T, class = void> struct make_integral_or_big_int_unsigned;
1029+
1030+
template <typename T>
1031+
struct make_integral_or_big_int_unsigned<
1032+
T, cpp::enable_if_t<cpp::is_integral_v<T>>> : cpp::make_unsigned<T> {};
1033+
1034+
template <typename T>
1035+
struct make_integral_or_big_int_unsigned<T, cpp::enable_if_t<is_big_int_v<T>>>
1036+
: make_big_int_unsigned<T> {};
1037+
1038+
template <typename T>
1039+
using make_integral_or_big_int_unsigned_t =
1040+
typename make_integral_or_big_int_unsigned<T>::type;
1041+
1042+
// make_integral_or_big_int_signed
1043+
template <typename T, class = void> struct make_integral_or_big_int_signed;
1044+
1045+
template <typename T>
1046+
struct make_integral_or_big_int_signed<T,
1047+
cpp::enable_if_t<cpp::is_integral_v<T>>>
1048+
: cpp::make_signed<T> {};
1049+
1050+
template <typename T>
1051+
struct make_integral_or_big_int_signed<T, cpp::enable_if_t<is_big_int_v<T>>>
1052+
: make_big_int_signed<T> {};
1053+
1054+
template <typename T>
1055+
using make_integral_or_big_int_signed_t =
1056+
typename make_integral_or_big_int_signed<T>::type;
1057+
9961058
namespace cpp {
9971059

9981060
// Specialization of cpp::bit_cast ('bit.h') from T to BigInt.

libc/src/__support/integer_to_string.h

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
#include "src/__support/CPP/span.h"
6868
#include "src/__support/CPP/string_view.h"
6969
#include "src/__support/CPP/type_traits.h"
70-
#include "src/__support/UInt.h" // is_big_int
70+
#include "src/__support/UInt.h" // make_integral_or_big_int_unsigned_t
7171
#include "src/__support/common.h"
7272

7373
namespace LIBC_NAMESPACE {
@@ -150,18 +150,6 @@ template <bool forward> class StringBufferWriterImpl {
150150
using StringBufferWriter = StringBufferWriterImpl<true>;
151151
using BackwardStringBufferWriter = StringBufferWriterImpl<false>;
152152

153-
template <typename T, class = void> struct IntegerWriterUnsigned {};
154-
155-
template <typename T>
156-
struct IntegerWriterUnsigned<T, cpp::enable_if_t<cpp::is_integral_v<T>>> {
157-
using type = cpp::make_unsigned_t<T>;
158-
};
159-
160-
template <typename T>
161-
struct IntegerWriterUnsigned<T, cpp::enable_if_t<is_big_int_v<T>>> {
162-
using type = typename T::unsigned_type;
163-
};
164-
165153
} // namespace details
166154

167155
namespace radix {
@@ -222,7 +210,7 @@ template <typename T, typename Fmt = radix::Dec> class IntegerToString {
222210
// An internal stateless structure that handles the number formatting logic.
223211
struct IntegerWriter {
224212
static_assert(cpp::is_integral_v<T> || is_big_int_v<T>);
225-
using UNSIGNED_T = typename details::IntegerWriterUnsigned<T>::type;
213+
using UNSIGNED_T = make_integral_or_big_int_unsigned_t<T>;
226214

227215
LIBC_INLINE static char digit_char(uint8_t digit) {
228216
if (digit < 10)

libc/src/__support/str_to_float.h

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,17 +1056,17 @@ hexadecimal_string_to_float(const char *__restrict src,
10561056
return output;
10571057
}
10581058

1059-
LIBC_INLINE uint64_t
1059+
template <class T>
1060+
LIBC_INLINE typename fputil::FPBits<T>::StorageType
10601061
nan_mantissa_from_ncharseq(const cpp::string_view ncharseq) {
1061-
uint64_t nan_mantissa = 0;
1062+
using FPBits = typename fputil::FPBits<T>;
1063+
using StorageType = typename FPBits::StorageType;
1064+
1065+
StorageType nan_mantissa = 0;
10621066

10631067
if (ncharseq.data() != nullptr && isdigit(ncharseq[0])) {
1064-
// This is to prevent errors when StorageType is larger than 64
1065-
// bits, since strtointeger only supports up to 64 bits. This is
1066-
// actually more than is required by the specification, which says
1067-
// for the input type "NAN(n-char-sequence)" that "the meaning of
1068-
// the n-char sequence is implementation-defined."
1069-
auto strtoint_result = strtointeger<uint64_t>(ncharseq.data(), 0);
1068+
StrToNumResult<StorageType> strtoint_result =
1069+
strtointeger<StorageType>(ncharseq.data(), 0);
10701070
if (!strtoint_result.has_error())
10711071
nan_mantissa = strtoint_result.value;
10721072

@@ -1172,9 +1172,8 @@ LIBC_INLINE StrToNumResult<T> strtofloatingpoint(const char *__restrict src) {
11721172
++index;
11731173
if (src[index] == ')') {
11741174
++index;
1175-
auto nan_mantissa_result = nan_mantissa_from_ncharseq(
1175+
nan_mantissa = nan_mantissa_from_ncharseq<T>(
11761176
cpp::string_view(src + (left_paren + 1), index - left_paren - 2));
1177-
nan_mantissa = static_cast<StorageType>(nan_mantissa_result);
11781177
} else {
11791178
index = left_paren;
11801179
}
@@ -1221,11 +1220,8 @@ template <class T> LIBC_INLINE StrToNumResult<T> strtonan(const char *arg) {
12211220
while (isalnum(arg[index]) || arg[index] == '_')
12221221
++index;
12231222

1224-
if (arg[index] == '\0') {
1225-
auto nan_mantissa_result =
1226-
nan_mantissa_from_ncharseq(cpp::string_view(arg, index));
1227-
nan_mantissa = static_cast<StorageType>(nan_mantissa_result);
1228-
}
1223+
if (arg[index] == '\0')
1224+
nan_mantissa = nan_mantissa_from_ncharseq<T>(cpp::string_view(arg, index));
12291225

12301226
result = FPBits::quiet_nan(fputil::Sign::POS, nan_mantissa);
12311227
return {result.get_val(), 0, error};

libc/src/__support/str_to_integer.h

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include "src/__support/CPP/limits.h"
1313
#include "src/__support/CPP/type_traits.h"
14+
#include "src/__support/UInt128.h"
1415
#include "src/__support/common.h"
1516
#include "src/__support/ctype_utils.h"
1617
#include "src/__support/str_to_num_result.h"
@@ -75,8 +76,12 @@ template <class T>
7576
LIBC_INLINE StrToNumResult<T>
7677
strtointeger(const char *__restrict src, int base,
7778
const size_t src_len = cpp::numeric_limits<size_t>::max()) {
78-
// TODO: Rewrite to support numbers longer than long long
79-
unsigned long long result = 0;
79+
using ResultType = typename cpp::conditional_t<(cpp::is_same_v<T, UInt128> ||
80+
cpp::is_same_v<T, Int128>),
81+
UInt128, unsigned long long>;
82+
83+
ResultType result = 0;
84+
8085
bool is_number = false;
8186
size_t src_cur = 0;
8287
int error_val = 0;
@@ -101,15 +106,16 @@ strtointeger(const char *__restrict src, int base,
101106
if (base == 16 && is_hex_start(src + src_cur, src_len - src_cur))
102107
src_cur = src_cur + 2;
103108

104-
constexpr bool IS_UNSIGNED = (cpp::numeric_limits<T>::min() == 0);
109+
constexpr bool IS_UNSIGNED = cpp::is_unsigned_v<T>;
105110
const bool is_positive = (result_sign == '+');
106-
unsigned long long constexpr NEGATIVE_MAX =
107-
!IS_UNSIGNED
108-
? static_cast<unsigned long long>(cpp::numeric_limits<T>::max()) + 1
109-
: cpp::numeric_limits<T>::max();
110-
unsigned long long const abs_max =
111+
112+
ResultType constexpr NEGATIVE_MAX =
113+
!IS_UNSIGNED ? static_cast<ResultType>(cpp::numeric_limits<T>::max()) + 1
114+
: cpp::numeric_limits<T>::max();
115+
ResultType const abs_max =
111116
(is_positive ? cpp::numeric_limits<T>::max() : NEGATIVE_MAX);
112-
unsigned long long const abs_max_div_by_base = abs_max / base;
117+
ResultType const abs_max_div_by_base = abs_max / base;
118+
113119
while (src_cur < src_len && isalnum(src[src_cur])) {
114120
int cur_digit = b36_char_to_int(src[src_cur]);
115121
if (cur_digit >= base)
@@ -149,10 +155,12 @@ strtointeger(const char *__restrict src, int base,
149155
return {cpp::numeric_limits<T>::min(), str_len, error_val};
150156
}
151157

152-
return {is_positive
153-
? static_cast<T>(result)
154-
: static_cast<T>(-static_cast<cpp::make_unsigned_t<T>>(result)),
155-
str_len, error_val};
158+
return {
159+
is_positive
160+
? static_cast<T>(result)
161+
: static_cast<T>(
162+
-static_cast<make_integral_or_big_int_unsigned_t<T>>(result)),
163+
str_len, error_val};
156164
}
157165

158166
} // namespace internal

libc/src/math/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ add_math_entrypoint_object(modff128)
190190
add_math_entrypoint_object(nan)
191191
add_math_entrypoint_object(nanf)
192192
add_math_entrypoint_object(nanl)
193+
add_math_entrypoint_object(nanf128)
193194

194195
add_math_entrypoint_object(nearbyint)
195196
add_math_entrypoint_object(nearbyintf)

libc/src/math/generic/CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1780,6 +1780,19 @@ add_entrypoint_object(
17801780
-O3
17811781
)
17821782

1783+
add_entrypoint_object(
1784+
nanf128
1785+
SRCS
1786+
nanf128.cpp
1787+
HDRS
1788+
../nanf128.h
1789+
DEPENDS
1790+
libc.src.__support.str_to_float
1791+
libc.src.errno.errno
1792+
COMPILE_OPTIONS
1793+
-O3
1794+
)
1795+
17831796
add_entrypoint_object(
17841797
nextafter
17851798
SRCS

libc/src/math/generic/nanf128.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//===-- Implementation of nanf128 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/nanf128.h"
10+
#include "src/__support/common.h"
11+
#include "src/__support/str_to_float.h"
12+
#include "src/errno/libc_errno.h"
13+
14+
namespace LIBC_NAMESPACE {
15+
16+
LLVM_LIBC_FUNCTION(float128, nanf128, (const char *arg)) {
17+
auto result = internal::strtonan<float128>(arg);
18+
if (result.has_error())
19+
libc_errno = result.error;
20+
return result.value;
21+
}
22+
23+
} // namespace LIBC_NAMESPACE

libc/src/math/nanf128.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//===-- Implementation header for nanf128 -----------------------*- 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_NANF128_H
10+
#define LLVM_LIBC_SRC_MATH_NANF128_H
11+
12+
#include "src/__support/macros/properties/types.h"
13+
14+
namespace LIBC_NAMESPACE {
15+
16+
float128 nanf128(const char *arg);
17+
18+
} // namespace LIBC_NAMESPACE
19+
20+
#endif // LLVM_LIBC_SRC_MATH_NANF128_H

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1506,6 +1506,22 @@ add_fp_unittest(
15061506
UNIT_TEST_ONLY
15071507
)
15081508

1509+
add_fp_unittest(
1510+
nanf128_test
1511+
SUITE
1512+
libc-math-smoke-tests
1513+
SRCS
1514+
nanf128_test.cpp
1515+
DEPENDS
1516+
libc.include.math
1517+
libc.include.signal
1518+
libc.src.math.nanf128
1519+
libc.src.__support.FPUtil.fp_bits
1520+
# FIXME: The nan tests currently have death tests, which aren't supported for
1521+
# hermetic tests.
1522+
UNIT_TEST_ONLY
1523+
)
1524+
15091525
add_fp_unittest(
15101526
nextafter_test
15111527
SUITE

0 commit comments

Comments
 (0)