[libc][math] Implement nan(f|l) functions (#76690)

Specification: https://en.cppreference.com/w/c/numeric/math/nan
This commit is contained in:
Nishant Mittal 2024-01-05 18:53:23 +05:30 committed by GitHub
parent f7f7574afe
commit 0504e93288
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 451 additions and 20 deletions

View File

@ -193,6 +193,9 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.modf
libc.src.math.modff
libc.src.math.modfl
libc.src.math.nan
libc.src.math.nanf
libc.src.math.nanl
libc.src.math.nearbyint
libc.src.math.nearbyintf
libc.src.math.nearbyintl

View File

@ -172,6 +172,9 @@ set(TARGET_LIBM_ENTRYPOINTS
#libc.src.math.modf
#libc.src.math.modff
#libc.src.math.modfl
#libc.src.math.nan
#libc.src.math.nanf
#libc.src.math.nanl
#libc.src.math.nearbyint
#libc.src.math.nearbyintf
#libc.src.math.nearbyintl

View File

@ -215,6 +215,8 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.lroundf
libc.src.math.modf
libc.src.math.modff
libc.src.math.nan
libc.src.math.nanf
libc.src.math.nearbyint
libc.src.math.nearbyintf
libc.src.math.nextafter

View File

@ -313,6 +313,9 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.modf
libc.src.math.modff
libc.src.math.modfl
libc.src.math.nan
libc.src.math.nanf
libc.src.math.nanl
libc.src.math.nearbyint
libc.src.math.nearbyintf
libc.src.math.nearbyintl

View File

@ -322,6 +322,9 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.modf
libc.src.math.modff
libc.src.math.modfl
libc.src.math.nan
libc.src.math.nanf
libc.src.math.nanl
libc.src.math.nearbyint
libc.src.math.nearbyintf
libc.src.math.nearbyintl

View File

@ -323,6 +323,9 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.modf
libc.src.math.modff
libc.src.math.modfl
libc.src.math.nan
libc.src.math.nanf
libc.src.math.nanl
libc.src.math.nearbyint
libc.src.math.nearbyintf
libc.src.math.nearbyintl

View File

@ -192,6 +192,9 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.modf
libc.src.math.modff
libc.src.math.modfl
libc.src.math.nan
libc.src.math.nanf
libc.src.math.nanl
libc.src.math.nearbyint
libc.src.math.nearbyintf
libc.src.math.nearbyintl

View File

@ -212,11 +212,11 @@ Basic Operations
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| modfl | |check| | |check| | | |check| | |check| | | | |check| | | | | |
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| nan | | | | | | | | | | | | |
| nan | |check| | |check| | | |check| | |check| | | | |check| | | | | |
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| nanf | | | | | | | | | | | | |
| nanf | |check| | |check| | | |check| | |check| | | | |check| | | | | |
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| nanl | | | | | | | | | | | | |
| nanl | |check| | |check| | | |check| | |check| | | | |check| | | | | |
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| nearbyint | |check| | |check| | | |check| | |check| | | | |check| | | | | |
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+

View File

@ -515,6 +515,10 @@ def StdC : StandardSpec<"stdc"> {
FunctionSpec<"scalbn", RetValSpec<DoubleType>, [ArgSpec<DoubleType>, ArgSpec<IntType>]>,
FunctionSpec<"scalbnf", RetValSpec<FloatType>, [ArgSpec<FloatType>, ArgSpec<IntType>]>,
FunctionSpec<"scalbnl", RetValSpec<LongDoubleType>, [ArgSpec<LongDoubleType>, ArgSpec<IntType>]>,
FunctionSpec<"nanf", RetValSpec<FloatType>, [ArgSpec<ConstCharPtr>]>,
FunctionSpec<"nan", RetValSpec<DoubleType>, [ArgSpec<ConstCharPtr>]>,
FunctionSpec<"nanl", RetValSpec<LongDoubleType>, [ArgSpec<ConstCharPtr>]>,
]
>;

View File

@ -12,6 +12,7 @@
#include "src/__support/CPP/bit.h"
#include "src/__support/CPP/limits.h"
#include "src/__support/CPP/optional.h"
#include "src/__support/CPP/string_view.h"
#include "src/__support/FPUtil/FEnvImpl.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/dyadic_float.h"
@ -1044,6 +1045,27 @@ hexadecimal_string_to_float(const char *__restrict src,
return output;
}
LIBC_INLINE uint64_t
nan_mantissa_from_ncharseq(const cpp::string_view ncharseq) {
uint64_t 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<uint64_t>(ncharseq.data(), 0);
if (!strtoint_result.has_error())
nan_mantissa = strtoint_result.value;
if (strtoint_result.parsed_len != static_cast<ptrdiff_t>(ncharseq.size()))
nan_mantissa = 0;
}
return nan_mantissa;
}
// Takes a pointer to a string and a pointer to a string pointer. This function
// is used as the backend for all of the string to float functions.
template <class T>
@ -1136,31 +1158,18 @@ LIBC_INLINE StrToNumResult<T> strtofloatingpoint(const char *__restrict src) {
++index;
if (src[index] == ')') {
++index;
if (isdigit(src[left_paren + 1])) {
// 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<uint64_t>(src + (left_paren + 1), 0);
if (strtoint_result.has_error()) {
error = strtoint_result.error;
}
nan_mantissa = static_cast<StorageType>(strtoint_result.value);
if (src[left_paren + 1 + strtoint_result.parsed_len] != ')')
nan_mantissa = 0;
}
auto nan_mantissa_result = nan_mantissa_from_ncharseq(
cpp::string_view(src + (left_paren + 1), index - left_paren - 2));
nan_mantissa = static_cast<StorageType>(nan_mantissa_result);
} else {
index = left_paren;
}
}
if (result.get_sign()) {
result = FPBits(result.build_quiet_nan(nan_mantissa));
result.set_sign(true);
} else {
result.set_sign(false);
result = FPBits(result.build_quiet_nan(nan_mantissa));
}
}
@ -1195,6 +1204,28 @@ LIBC_INLINE StrToNumResult<T> strtofloatingpoint(const char *__restrict src) {
return {T(result), index, error};
}
template <class T> LIBC_INLINE StrToNumResult<T> strtonan(const char *arg) {
using FPBits = typename fputil::FPBits<T>;
using StorageType = typename FPBits::StorageType;
FPBits result;
int error = 0;
StorageType nan_mantissa = 0;
ptrdiff_t index = 0;
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<StorageType>(nan_mantissa_result);
}
result = FPBits(result.build_quiet_nan(nan_mantissa));
return {T(result), 0, error};
}
} // namespace internal
} // namespace LIBC_NAMESPACE

View File

@ -179,6 +179,10 @@ add_math_entrypoint_object(modf)
add_math_entrypoint_object(modff)
add_math_entrypoint_object(modfl)
add_math_entrypoint_object(nan)
add_math_entrypoint_object(nanf)
add_math_entrypoint_object(nanl)
add_math_entrypoint_object(nearbyint)
add_math_entrypoint_object(nearbyintf)
add_math_entrypoint_object(nearbyintl)

View File

@ -1437,6 +1437,45 @@ add_entrypoint_object(
-O3
)
add_entrypoint_object(
nan
SRCS
nan.cpp
HDRS
../nan.h
DEPENDS
libc.src.__support.str_to_float
libc.src.errno.errno
COMPILE_OPTIONS
-O3
)
add_entrypoint_object(
nanf
SRCS
nanf.cpp
HDRS
../nanf.h
DEPENDS
libc.src.__support.str_to_float
libc.src.errno.errno
COMPILE_OPTIONS
-O3
)
add_entrypoint_object(
nanl
SRCS
nanl.cpp
HDRS
../nanl.h
DEPENDS
libc.src.__support.str_to_float
libc.src.errno.errno
COMPILE_OPTIONS
-O3
)
add_entrypoint_object(
nextafter
SRCS

View File

@ -0,0 +1,23 @@
//===-- Implementation of nan 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/nan.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(double, nan, (const char *arg)) {
auto result = internal::strtonan<double>(arg);
if (result.has_error())
libc_errno = result.error;
return result.value;
}
} // namespace LIBC_NAMESPACE

View File

@ -0,0 +1,23 @@
//===-- Implementation of nanf 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/nanf.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(float, nanf, (const char *arg)) {
auto result = internal::strtonan<float>(arg);
if (result.has_error())
libc_errno = result.error;
return result.value;
}
} // namespace LIBC_NAMESPACE

View File

@ -0,0 +1,23 @@
//===-- Implementation of nanl 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/nanl.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(long double, nanl, (const char *arg)) {
auto result = internal::strtonan<long double>(arg);
if (result.has_error())
libc_errno = result.error;
return result.value;
}
} // namespace LIBC_NAMESPACE

18
libc/src/math/nan.h Normal file
View File

@ -0,0 +1,18 @@
//===-- Implementation header for nan ---------------------------*- 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_NAN_H
#define LLVM_LIBC_SRC_MATH_NAN_H
namespace LIBC_NAMESPACE {
double nan(const char *arg);
} // namespace LIBC_NAMESPACE
#endif // LLVM_LIBC_SRC_MATH_NAN_H

18
libc/src/math/nanf.h Normal file
View File

@ -0,0 +1,18 @@
//===-- Implementation header for nanf --------------------------*- 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_NANF_H
#define LLVM_LIBC_SRC_MATH_NANF_H
namespace LIBC_NAMESPACE {
float nanf(const char *arg);
} // namespace LIBC_NAMESPACE
#endif // LLVM_LIBC_SRC_MATH_NANF_H

18
libc/src/math/nanl.h Normal file
View File

@ -0,0 +1,18 @@
//===-- Implementation header for nanl --------------------------*- 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_NANL_H
#define LLVM_LIBC_SRC_MATH_NANL_H
namespace LIBC_NAMESPACE {
long double nanl(const char *arg);
} // namespace LIBC_NAMESPACE
#endif // LLVM_LIBC_SRC_MATH_NANL_H

View File

@ -1203,6 +1203,45 @@ add_fp_unittest(
libc.src.__support.FPUtil.fp_bits
)
add_fp_unittest(
nanf_test
SUITE
libc-math-smoke-tests
SRCS
nanf_test.cpp
DEPENDS
libc.include.math
libc.include.signal
libc.src.math.nanf
libc.src.__support.FPUtil.fp_bits
)
add_fp_unittest(
nan_test
SUITE
libc-math-smoke-tests
SRCS
nan_test.cpp
DEPENDS
libc.include.math
libc.include.signal
libc.src.math.nan
libc.src.__support.FPUtil.fp_bits
)
add_fp_unittest(
nanl_test
SUITE
libc-math-smoke-tests
SRCS
nanl_test.cpp
DEPENDS
libc.include.math
libc.include.signal
libc.src.math.nanl
libc.src.__support.FPUtil.fp_bits
)
# FIXME: These tests are currently spurious for NVPTX.
if(NOT LIBC_GPU_TARGET_ARCHITECTURE_IS_NVPTX)
add_fp_unittest(

View File

@ -0,0 +1,47 @@
//===-- Unittests for nan -------------------------------------------------===//
//
// 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/math/nan.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"
#include <signal.h>
class LlvmLibcNanTest : public LIBC_NAMESPACE::testing::Test {
public:
using StorageType = LIBC_NAMESPACE::fputil::FPBits<double>::StorageType;
void run_test(const char *input_str, StorageType bits) {
double result = LIBC_NAMESPACE::nan(input_str);
auto actual_fp = LIBC_NAMESPACE::fputil::FPBits<double>(result);
auto expected_fp = LIBC_NAMESPACE::fputil::FPBits<double>(bits);
EXPECT_EQ(actual_fp.bits, expected_fp.bits);
};
};
TEST_F(LlvmLibcNanTest, NCharSeq) {
run_test("", 0x7ff8000000000000);
run_test("1234", 0x7ff80000000004d2);
run_test("0x1234", 0x7ff8000000001234);
run_test("1a", 0x7ff8000000000000);
run_test("1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM_",
0x7ff8000000000000);
run_test("10000000000000000000000000000000000000000000000000",
0x7ff8000000000000);
}
TEST_F(LlvmLibcNanTest, RandomString) {
run_test(" 1234", 0x7ff8000000000000);
run_test("-1234", 0x7ff8000000000000);
run_test("asd&f", 0x7ff8000000000000);
run_test("123 ", 0x7ff8000000000000);
}
TEST_F(LlvmLibcNanTest, InvalidInput) {
EXPECT_DEATH([] { LIBC_NAMESPACE::nan(nullptr); }, WITH_SIGNAL(SIGSEGV));
}

View File

@ -0,0 +1,46 @@
//===-- Unittests for nanf ------------------------------------------------===//
//
// 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/math/nanf.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"
#include <signal.h>
class LlvmLibcNanfTest : public LIBC_NAMESPACE::testing::Test {
public:
using StorageType = LIBC_NAMESPACE::fputil::FPBits<float>::StorageType;
void run_test(const char *input_str, StorageType bits) {
float result = LIBC_NAMESPACE::nanf(input_str);
auto actual_fp = LIBC_NAMESPACE::fputil::FPBits<float>(result);
auto expected_fp = LIBC_NAMESPACE::fputil::FPBits<float>(bits);
EXPECT_EQ(actual_fp.bits, expected_fp.bits);
};
};
TEST_F(LlvmLibcNanfTest, NCharSeq) {
run_test("", 0x7fc00000);
run_test("1234", 0x7fc004d2);
run_test("0x1234", 0x7fc01234);
run_test("1a", 0x7fc00000);
run_test("1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM_",
0x7fc00000);
run_test("10000000000000000000000000000000000000000000000000", 0x7fc00000);
}
TEST_F(LlvmLibcNanfTest, RandomString) {
run_test(" 1234", 0x7fc00000);
run_test("-1234", 0x7fc00000);
run_test("asd&f", 0x7fc00000);
run_test("123 ", 0x7fc00000);
}
TEST_F(LlvmLibcNanfTest, InvalidInput) {
EXPECT_DEATH([] { LIBC_NAMESPACE::nanf(nullptr); }, WITH_SIGNAL(SIGSEGV));
}

View File

@ -0,0 +1,72 @@
//===-- Unittests for nanl ------------------------------------------------===//
//
// 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/math/nanl.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"
#include <signal.h>
#if defined(LIBC_LONG_DOUBLE_IS_FLOAT64)
#define SELECT_LONG_DOUBLE(val, _, __) val
#elif defined(LIBC_LONG_DOUBLE_IS_X86_FLOAT80)
#define SELECT_LONG_DOUBLE(_, val, __) val
#else
#define SELECT_LONG_DOUBLE(_, __, val) val
#endif
class LlvmLibcNanlTest : public LIBC_NAMESPACE::testing::Test {
public:
using StorageType = LIBC_NAMESPACE::fputil::FPBits<long double>::StorageType;
void run_test(const char *input_str, StorageType bits) {
long double result = LIBC_NAMESPACE::nanl(input_str);
auto actual_fp = LIBC_NAMESPACE::fputil::FPBits<long double>(result);
auto expected_fp = LIBC_NAMESPACE::fputil::FPBits<long double>(bits);
EXPECT_EQ(actual_fp.bits, expected_fp.bits);
};
};
TEST_F(LlvmLibcNanlTest, NCharSeq) {
run_test("",
SELECT_LONG_DOUBLE(0x7ff8000000000000, (UInt128(0x7fffc00000) << 40),
(UInt128(0x7fff800000000000) << 64)));
run_test("1234", SELECT_LONG_DOUBLE(
0x7ff80000000004d2,
(UInt128(0x7fffc00000) << 40) + UInt128(0x4d2),
(UInt128(0x7fff800000000000) << 64) + UInt128(0x4d2)));
run_test("0x1234",
SELECT_LONG_DOUBLE(0x7ff8000000001234,
(UInt128(0x7fffc00000) << 40) + UInt128(0x1234),
(UInt128(0x7fff800000000000) << 64) +
UInt128(0x1234)));
run_test("1a",
SELECT_LONG_DOUBLE(0x7ff8000000000000, (UInt128(0x7fffc00000) << 40),
(UInt128(0x7fff800000000000) << 64)));
run_test("1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM_",
SELECT_LONG_DOUBLE(0x7ff8000000000000, (UInt128(0x7fffc00000) << 40),
(UInt128(0x7fff800000000000) << 64)));
run_test("10000000000000000000000000000000000000000000000000",
SELECT_LONG_DOUBLE(0x7ff8000000000000, (UInt128(0x7fffc00000) << 40),
(UInt128(0x7fff800000000000) << 64)));
}
TEST_F(LlvmLibcNanlTest, RandomString) {
StorageType expected =
SELECT_LONG_DOUBLE(0x7ff8000000000000, (UInt128(0x7fffc00000) << 40),
(UInt128(0x7fff800000000000) << 64));
run_test(" 1234", expected);
run_test("-1234", expected);
run_test("asd&f", expected);
run_test("123 ", expected);
}
TEST_F(LlvmLibcNanlTest, InvalidInput) {
EXPECT_DEATH([] { LIBC_NAMESPACE::nanl(nullptr); }, WITH_SIGNAL(SIGSEGV));
}

View File

@ -1874,6 +1874,12 @@ libc_math_function(name = "llroundf")
libc_math_function(name = "llroundl")
libc_math_function(name = "nan")
libc_math_function(name = "nanf")
libc_math_function(name = "nanl")
libc_math_function(name = "nearbyint")
libc_math_function(name = "nearbyintf")