diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt index 7d69099e3cb9d..43c9e81f17833 100644 --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -468,6 +468,7 @@ if(LIBC_TYPES_HAS_FLOAT128) libc.src.math.lrintf128 libc.src.math.lroundf128 libc.src.math.modff128 + libc.src.math.nanf128 libc.src.math.nextafterf128 libc.src.math.rintf128 libc.src.math.roundf128 diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt index b1c9dd0428eea..99ef84d3f7397 100644 --- a/libc/config/linux/riscv/entrypoints.txt +++ b/libc/config/linux/riscv/entrypoints.txt @@ -476,6 +476,7 @@ if(LIBC_TYPES_HAS_FLOAT128) libc.src.math.lrintf128 libc.src.math.lroundf128 libc.src.math.modff128 + libc.src.math.nanf128 libc.src.math.nextafterf128 libc.src.math.rintf128 libc.src.math.roundf128 diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index 4fb31c593b9dc..a8b416aa9a0cd 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -481,6 +481,7 @@ if(LIBC_TYPES_HAS_FLOAT128) libc.src.math.lrintf128 libc.src.math.lroundf128 libc.src.math.modff128 + libc.src.math.nanf128 libc.src.math.nextafterf128 libc.src.math.rintf128 libc.src.math.roundf128 diff --git a/libc/docs/math/index.rst b/libc/docs/math/index.rst index ed54a7d091ecb..3240a8c5d2456 100644 --- a/libc/docs/math/index.rst +++ b/libc/docs/math/index.rst @@ -259,6 +259,8 @@ Basic Operations +--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+ | nanl | |check| | |check| | |check| | |check| | |check| | | | |check| | |check| | |check| | | | +--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+ +| nanf128 | |check| | |check| | | |check| | | | | | | | | | ++--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+ | nearbyint | |check| | |check| | |check| | |check| | |check| | | | |check| | |check| | |check| | | | +--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+ | nearbyintf | |check| | |check| | |check| | |check| | |check| | | | |check| | |check| | |check| | | | diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td index afe01b1bb6856..2bc9bc8b9b1a6 100644 --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -559,6 +559,7 @@ def StdC : StandardSpec<"stdc"> { FunctionSpec<"nanf", RetValSpec, [ArgSpec]>, FunctionSpec<"nan", RetValSpec, [ArgSpec]>, FunctionSpec<"nanl", RetValSpec, [ArgSpec]>, + GuardedFunctionSpec<"nanf128", RetValSpec, [ArgSpec], "LIBC_TYPES_HAS_FLOAT128">, ] >; diff --git a/libc/src/__support/UInt.h b/libc/src/__support/UInt.h index d92d61ed094eb..df01e081e3c19 100644 --- a/libc/src/__support/UInt.h +++ b/libc/src/__support/UInt.h @@ -993,6 +993,68 @@ struct is_big_int> : cpp::true_type {}; template LIBC_INLINE_VAR constexpr bool is_big_int_v = is_big_int::value; +// extensions of type traits to include BigInt + +// is_integral_or_big_int +template +struct is_integral_or_big_int + : cpp::bool_constant<(cpp::is_integral_v || is_big_int_v)> {}; + +template +LIBC_INLINE_VAR constexpr bool is_integral_or_big_int_v = + is_integral_or_big_int::value; + +// make_big_int_unsigned +template struct make_big_int_unsigned; + +template +struct make_big_int_unsigned> + : cpp::type_identity> {}; + +template +using make_big_int_unsigned_t = typename make_big_int_unsigned::type; + +// make_big_int_signed +template struct make_big_int_signed; + +template +struct make_big_int_signed> + : cpp::type_identity> {}; + +template +using make_big_int_signed_t = typename make_big_int_signed::type; + +// make_integral_or_big_int_unsigned +template struct make_integral_or_big_int_unsigned; + +template +struct make_integral_or_big_int_unsigned< + T, cpp::enable_if_t>> : cpp::make_unsigned {}; + +template +struct make_integral_or_big_int_unsigned>> + : make_big_int_unsigned {}; + +template +using make_integral_or_big_int_unsigned_t = + typename make_integral_or_big_int_unsigned::type; + +// make_integral_or_big_int_signed +template struct make_integral_or_big_int_signed; + +template +struct make_integral_or_big_int_signed>> + : cpp::make_signed {}; + +template +struct make_integral_or_big_int_signed>> + : make_big_int_signed {}; + +template +using make_integral_or_big_int_signed_t = + typename make_integral_or_big_int_signed::type; + namespace cpp { // Specialization of cpp::bit_cast ('bit.h') from T to BigInt. diff --git a/libc/src/__support/integer_to_string.h b/libc/src/__support/integer_to_string.h index ac0bdd688aeab..f72d00d1a7456 100644 --- a/libc/src/__support/integer_to_string.h +++ b/libc/src/__support/integer_to_string.h @@ -67,7 +67,7 @@ #include "src/__support/CPP/span.h" #include "src/__support/CPP/string_view.h" #include "src/__support/CPP/type_traits.h" -#include "src/__support/UInt.h" // is_big_int +#include "src/__support/UInt.h" // make_integral_or_big_int_unsigned_t #include "src/__support/common.h" namespace LIBC_NAMESPACE { @@ -150,18 +150,6 @@ template class StringBufferWriterImpl { using StringBufferWriter = StringBufferWriterImpl; using BackwardStringBufferWriter = StringBufferWriterImpl; -template struct IntegerWriterUnsigned {}; - -template -struct IntegerWriterUnsigned>> { - using type = cpp::make_unsigned_t; -}; - -template -struct IntegerWriterUnsigned>> { - using type = typename T::unsigned_type; -}; - } // namespace details namespace radix { @@ -222,7 +210,7 @@ template class IntegerToString { // An internal stateless structure that handles the number formatting logic. struct IntegerWriter { static_assert(cpp::is_integral_v || is_big_int_v); - using UNSIGNED_T = typename details::IntegerWriterUnsigned::type; + using UNSIGNED_T = make_integral_or_big_int_unsigned_t; LIBC_INLINE static char digit_char(uint8_t digit) { if (digit < 10) diff --git a/libc/src/__support/str_to_float.h b/libc/src/__support/str_to_float.h index 073e1dc672172..2cf2cfb027243 100644 --- a/libc/src/__support/str_to_float.h +++ b/libc/src/__support/str_to_float.h @@ -1056,17 +1056,17 @@ hexadecimal_string_to_float(const char *__restrict src, return output; } -LIBC_INLINE uint64_t +template +LIBC_INLINE typename fputil::FPBits::StorageType nan_mantissa_from_ncharseq(const cpp::string_view ncharseq) { - uint64_t nan_mantissa = 0; + using FPBits = typename fputil::FPBits; + using StorageType = typename FPBits::StorageType; + + StorageType nan_mantissa = 0; if (ncharseq.data() != nullptr && isdigit(ncharseq[0])) { - // This is to prevent errors when StorageType is larger than 64 - // bits, since strtointeger only supports up to 64 bits. This is - // actually more than is required by the specification, which says - // for the input type "NAN(n-char-sequence)" that "the meaning of - // the n-char sequence is implementation-defined." - auto strtoint_result = strtointeger(ncharseq.data(), 0); + StrToNumResult strtoint_result = + strtointeger(ncharseq.data(), 0); if (!strtoint_result.has_error()) nan_mantissa = strtoint_result.value; @@ -1172,9 +1172,8 @@ LIBC_INLINE StrToNumResult strtofloatingpoint(const char *__restrict src) { ++index; if (src[index] == ')') { ++index; - auto nan_mantissa_result = nan_mantissa_from_ncharseq( + nan_mantissa = nan_mantissa_from_ncharseq( cpp::string_view(src + (left_paren + 1), index - left_paren - 2)); - nan_mantissa = static_cast(nan_mantissa_result); } else { index = left_paren; } @@ -1221,11 +1220,8 @@ template LIBC_INLINE StrToNumResult strtonan(const char *arg) { while (isalnum(arg[index]) || arg[index] == '_') ++index; - if (arg[index] == '\0') { - auto nan_mantissa_result = - nan_mantissa_from_ncharseq(cpp::string_view(arg, index)); - nan_mantissa = static_cast(nan_mantissa_result); - } + if (arg[index] == '\0') + nan_mantissa = nan_mantissa_from_ncharseq(cpp::string_view(arg, index)); result = FPBits::quiet_nan(fputil::Sign::POS, nan_mantissa); return {result.get_val(), 0, error}; diff --git a/libc/src/__support/str_to_integer.h b/libc/src/__support/str_to_integer.h index b87808993fee5..02c71d40a1c0a 100644 --- a/libc/src/__support/str_to_integer.h +++ b/libc/src/__support/str_to_integer.h @@ -11,6 +11,7 @@ #include "src/__support/CPP/limits.h" #include "src/__support/CPP/type_traits.h" +#include "src/__support/UInt128.h" #include "src/__support/common.h" #include "src/__support/ctype_utils.h" #include "src/__support/str_to_num_result.h" @@ -75,8 +76,12 @@ template LIBC_INLINE StrToNumResult strtointeger(const char *__restrict src, int base, const size_t src_len = cpp::numeric_limits::max()) { - // TODO: Rewrite to support numbers longer than long long - unsigned long long result = 0; + using ResultType = typename cpp::conditional_t<(cpp::is_same_v || + cpp::is_same_v), + UInt128, unsigned long long>; + + ResultType result = 0; + bool is_number = false; size_t src_cur = 0; int error_val = 0; @@ -101,15 +106,16 @@ strtointeger(const char *__restrict src, int base, if (base == 16 && is_hex_start(src + src_cur, src_len - src_cur)) src_cur = src_cur + 2; - constexpr bool IS_UNSIGNED = (cpp::numeric_limits::min() == 0); + constexpr bool IS_UNSIGNED = cpp::is_unsigned_v; const bool is_positive = (result_sign == '+'); - unsigned long long constexpr NEGATIVE_MAX = - !IS_UNSIGNED - ? static_cast(cpp::numeric_limits::max()) + 1 - : cpp::numeric_limits::max(); - unsigned long long const abs_max = + + ResultType constexpr NEGATIVE_MAX = + !IS_UNSIGNED ? static_cast(cpp::numeric_limits::max()) + 1 + : cpp::numeric_limits::max(); + ResultType const abs_max = (is_positive ? cpp::numeric_limits::max() : NEGATIVE_MAX); - unsigned long long const abs_max_div_by_base = abs_max / base; + ResultType const abs_max_div_by_base = abs_max / base; + while (src_cur < src_len && isalnum(src[src_cur])) { int cur_digit = b36_char_to_int(src[src_cur]); if (cur_digit >= base) @@ -149,10 +155,12 @@ strtointeger(const char *__restrict src, int base, return {cpp::numeric_limits::min(), str_len, error_val}; } - return {is_positive - ? static_cast(result) - : static_cast(-static_cast>(result)), - str_len, error_val}; + return { + is_positive + ? static_cast(result) + : static_cast( + -static_cast>(result)), + str_len, error_val}; } } // namespace internal diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt index 750fd5f0e3a9b..cd03065399480 100644 --- a/libc/src/math/CMakeLists.txt +++ b/libc/src/math/CMakeLists.txt @@ -190,6 +190,7 @@ add_math_entrypoint_object(modff128) add_math_entrypoint_object(nan) add_math_entrypoint_object(nanf) add_math_entrypoint_object(nanl) +add_math_entrypoint_object(nanf128) add_math_entrypoint_object(nearbyint) add_math_entrypoint_object(nearbyintf) diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt index 667381d615d1e..87f53105a1b31 100644 --- a/libc/src/math/generic/CMakeLists.txt +++ b/libc/src/math/generic/CMakeLists.txt @@ -1780,6 +1780,19 @@ add_entrypoint_object( -O3 ) +add_entrypoint_object( + nanf128 + SRCS + nanf128.cpp + HDRS + ../nanf128.h + DEPENDS + libc.src.__support.str_to_float + libc.src.errno.errno + COMPILE_OPTIONS + -O3 +) + add_entrypoint_object( nextafter SRCS diff --git a/libc/src/math/generic/nanf128.cpp b/libc/src/math/generic/nanf128.cpp new file mode 100644 index 0000000000000..f087c9f074fde --- /dev/null +++ b/libc/src/math/generic/nanf128.cpp @@ -0,0 +1,23 @@ +//===-- Implementation of nanf128 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/nanf128.h" +#include "src/__support/common.h" +#include "src/__support/str_to_float.h" +#include "src/errno/libc_errno.h" + +namespace LIBC_NAMESPACE { + +LLVM_LIBC_FUNCTION(float128, nanf128, (const char *arg)) { + auto result = internal::strtonan(arg); + if (result.has_error()) + libc_errno = result.error; + return result.value; +} + +} // namespace LIBC_NAMESPACE diff --git a/libc/src/math/nanf128.h b/libc/src/math/nanf128.h new file mode 100644 index 0000000000000..b06d14e2f945e --- /dev/null +++ b/libc/src/math/nanf128.h @@ -0,0 +1,20 @@ +//===-- Implementation header for nanf128 -----------------------*- 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_NANF128_H +#define LLVM_LIBC_SRC_MATH_NANF128_H + +#include "src/__support/macros/properties/types.h" + +namespace LIBC_NAMESPACE { + +float128 nanf128(const char *arg); + +} // namespace LIBC_NAMESPACE + +#endif // LLVM_LIBC_SRC_MATH_NANF128_H diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt index 293e65abd44f5..80ea9d1109a0c 100644 --- a/libc/test/src/math/smoke/CMakeLists.txt +++ b/libc/test/src/math/smoke/CMakeLists.txt @@ -1506,6 +1506,22 @@ add_fp_unittest( UNIT_TEST_ONLY ) +add_fp_unittest( + nanf128_test + SUITE + libc-math-smoke-tests + SRCS + nanf128_test.cpp + DEPENDS + libc.include.math + libc.include.signal + libc.src.math.nanf128 + libc.src.__support.FPUtil.fp_bits + # FIXME: The nan tests currently have death tests, which aren't supported for + # hermetic tests. + UNIT_TEST_ONLY +) + add_fp_unittest( nextafter_test SUITE diff --git a/libc/test/src/math/smoke/nanf128_test.cpp b/libc/test/src/math/smoke/nanf128_test.cpp new file mode 100644 index 0000000000000..2a9f57de5b43b --- /dev/null +++ b/libc/test/src/math/smoke/nanf128_test.cpp @@ -0,0 +1,60 @@ +//===-- Unittests for nanf128 ---------------------------------------------===// +// +// 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/__support/FPUtil/FPBits.h" +#include "src/__support/UInt128.h" +#include "src/math/nanf128.h" +#include "test/UnitTest/FPMatcher.h" +#include "test/UnitTest/Test.h" + +class LlvmLibcNanf128Test : public LIBC_NAMESPACE::testing::Test { +public: + using FPBits128 = LIBC_NAMESPACE::fputil::FPBits; + using StorageType = FPBits128::StorageType; + + const UInt128 QUIET_NAN = FPBits128::quiet_nan().uintval(); + const UInt128 ONE = UInt128(1); + + void run_test(const char *input_str, StorageType bits) { + float128 result = LIBC_NAMESPACE::nanf128(input_str); + auto actual_fp = FPBits128(result); + auto expected_fp = FPBits128(bits); + EXPECT_EQ(actual_fp.uintval(), expected_fp.uintval()); + }; +}; + +TEST_F(LlvmLibcNanf128Test, NCharSeq) { + run_test("", QUIET_NAN); + run_test("1234", QUIET_NAN | 1234); + run_test("0x1234", QUIET_NAN | 0x1234); + run_test("2417851639229258349412352", QUIET_NAN | (ONE << 81)); + run_test("0x200000000000000000000", QUIET_NAN | (ONE << 81)); + run_test("10384593717069655257060992658440191", + QUIET_NAN | FPBits128::SIG_MASK); + run_test("0x1ffffffffffffffffffffffffffff", QUIET_NAN | FPBits128::SIG_MASK); + run_test("10384593717069655257060992658440192", QUIET_NAN); + run_test("0x20000000000000000000000000000", QUIET_NAN); + run_test("1a", QUIET_NAN); + run_test("10000000000000000000000000000000000000000000000000", QUIET_NAN); +} + +TEST_F(LlvmLibcNanf128Test, RandomString) { + run_test(" 1234", QUIET_NAN); + run_test("-1234", QUIET_NAN); + run_test("asd&f", QUIET_NAN); + run_test("123 ", QUIET_NAN); + run_test("1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM_", + QUIET_NAN); +} + +#ifndef LIBC_HAVE_ADDRESS_SANITIZER +#include +TEST_F(LlvmLibcNanf128Test, InvalidInput) { + EXPECT_DEATH([] { LIBC_NAMESPACE::nanf128(nullptr); }, WITH_SIGNAL(SIGSEGV)); +} +#endif // LIBC_HAVE_ADDRESS_SANITIZER