-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[libc] Implement wcstod and wcstold. #168020
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
Conversation
These are simply implemented as specializations of strtofloatingpoint for double / long double and for wchar_t. The unit tests are copied from the strtod / strtold ones.
|
@llvm/pr-subscribers-libc Author: Alexey Samsonov (vonosmas) ChangesThese are simply implemented as specializations of strtofloatingpoint for double / long double and for wchar_t. The unit tests are copied from the strtod / strtold ones. Patch is 60.14 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/168020.diff 10 Files Affected:
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index d3bcad470b3e1..5036c9438a503 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -398,9 +398,11 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.wchar.wmemchr
libc.src.wchar.wcpcpy
libc.src.wchar.wcpncpy
+ libc.src.wchar.wcstod
libc.src.wchar.wcstof
libc.src.wchar.wcstok
libc.src.wchar.wcstol
+ libc.src.wchar.wcstold
libc.src.wchar.wcstoll
libc.src.wchar.wcstoul
libc.src.wchar.wcstoull
diff --git a/libc/include/wchar.yaml b/libc/include/wchar.yaml
index faceb9bb4e12d..a524c7f56bed0 100644
--- a/libc/include/wchar.yaml
+++ b/libc/include/wchar.yaml
@@ -367,3 +367,17 @@ functions:
arguments:
- type: const wchar_t *__restrict
- type: wchar_t **__restrict
+ - name: wcstod
+ standards:
+ - stdc
+ return_type: double
+ arguments:
+ - type: const wchar_t *__restrict
+ - type: wchar_t **__restrict
+ - name: wcstold
+ standards:
+ - stdc
+ return_type: long double
+ arguments:
+ - type: const wchar_t *__restrict
+ - type: wchar_t **__restrict
diff --git a/libc/src/wchar/CMakeLists.txt b/libc/src/wchar/CMakeLists.txt
index e3fac9fb80529..e6d9af9eacf73 100644
--- a/libc/src/wchar/CMakeLists.txt
+++ b/libc/src/wchar/CMakeLists.txt
@@ -110,6 +110,28 @@ add_entrypoint_object(
libc.src.errno.errno
)
+add_entrypoint_object(
+ wcstod
+ SRCS
+ wcstod.cpp
+ HDRS
+ wcstod.h
+ DEPENDS
+ libc.src.__support.str_to_float
+ libc.src.errno.errno
+)
+
+add_entrypoint_object(
+ wcstold
+ SRCS
+ wcstold.cpp
+ HDRS
+ wcstold.h
+ DEPENDS
+ libc.src.__support.str_to_float
+ libc.src.errno.errno
+)
+
add_entrypoint_object(
wcstok
SRCS
diff --git a/libc/src/wchar/wcstod.cpp b/libc/src/wchar/wcstod.cpp
new file mode 100644
index 0000000000000..95351c304c0ff
--- /dev/null
+++ b/libc/src/wchar/wcstod.cpp
@@ -0,0 +1,30 @@
+//===-- Implementation of wcstod ------------------------------------------===//
+//
+// 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/wchar/wcstod.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/str_to_float.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(double, wcstod,
+ (const wchar_t *__restrict str,
+ wchar_t **__restrict str_end)) {
+ auto result = internal::strtofloatingpoint<double>(str);
+ if (result.has_error())
+ libc_errno = result.error;
+
+ if (str_end != nullptr)
+ *str_end = const_cast<wchar_t *>(str + result.parsed_len);
+
+ return result.value;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/wchar/wcstod.h b/libc/src/wchar/wcstod.h
new file mode 100644
index 0000000000000..ff397b93d405d
--- /dev/null
+++ b/libc/src/wchar/wcstod.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for wcstod ------------------------*- 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_WCHAR_WCSTOD_H
+#define LLVM_LIBC_SRC_WCHAR_WCSTOD_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+double wcstod(const wchar_t *__restrict str, wchar_t **__restrict str_end);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_WCHAR_WCSTOD_H
diff --git a/libc/src/wchar/wcstold.cpp b/libc/src/wchar/wcstold.cpp
new file mode 100644
index 0000000000000..ffbc3f248b883
--- /dev/null
+++ b/libc/src/wchar/wcstold.cpp
@@ -0,0 +1,30 @@
+//===-- Implementation of wcstold -----------------------------------------===//
+//
+// 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/wchar/wcstold.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/str_to_float.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(long double, wcstold,
+ (const wchar_t *__restrict str,
+ wchar_t **__restrict str_end)) {
+ auto result = internal::strtofloatingpoint<long double>(str);
+ if (result.has_error())
+ libc_errno = result.error;
+
+ if (str_end != nullptr)
+ *str_end = const_cast<wchar_t *>(str + result.parsed_len);
+
+ return result.value;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/wchar/wcstold.h b/libc/src/wchar/wcstold.h
new file mode 100644
index 0000000000000..1525362b33571
--- /dev/null
+++ b/libc/src/wchar/wcstold.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for wcstold -----------------------*- 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_WCHAR_WCSTOLD_H
+#define LLVM_LIBC_SRC_WCHAR_WCSTOLD_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+long double wcstold(const wchar_t *__restrict str,
+ wchar_t **__restrict str_end);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_WCHAR_WCSTOLD_H
diff --git a/libc/test/src/wchar/CMakeLists.txt b/libc/test/src/wchar/CMakeLists.txt
index 122cad2575327..a62a30fe00124 100644
--- a/libc/test/src/wchar/CMakeLists.txt
+++ b/libc/test/src/wchar/CMakeLists.txt
@@ -538,5 +538,32 @@ add_libc_test(
DEPENDS
libc.src.wchar.wcstof
libc.test.UnitTest.ErrnoCheckingTest
- libc.test.UnitTest.LibcFPTestHelpers
+ LINK_LIBRARIES
+ LibcFPTestHelpers
+)
+
+add_libc_test(
+ wcstod_test
+ SUITE
+ libc_wchar_unittests
+ SRCS
+ wcstod_test.cpp
+ DEPENDS
+ libc.src.wchar.wcstod
+ libc.test.UnitTest.ErrnoCheckingTest
+ LINK_LIBRARIES
+ LibcFPTestHelpers
+)
+
+add_libc_test(
+ wcstold_test
+ SUITE
+ libc_wchar_unittests
+ SRCS
+ wcstold_test.cpp
+ DEPENDS
+ libc.src.__support.FPUtil.fp_bits
+ libc.src.__support.uint128
+ libc.src.wchar.wcstold
+ libc.test.UnitTest.ErrnoCheckingTest
)
diff --git a/libc/test/src/wchar/wcstod_test.cpp b/libc/test/src/wchar/wcstod_test.cpp
new file mode 100644
index 0000000000000..ac6c48d39d287
--- /dev/null
+++ b/libc/test/src/wchar/wcstod_test.cpp
@@ -0,0 +1,585 @@
+//===-- Unittests for wcstod ----------------------------------------------===//
+//
+// 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/wchar/wcstod.h"
+
+#include "src/__support/FPUtil/FPBits.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/RoundingModeUtils.h"
+#include "test/UnitTest/Test.h"
+
+#include <stddef.h>
+
+using LIBC_NAMESPACE::fputil::testing::ForceRoundingModeTest;
+using LIBC_NAMESPACE::fputil::testing::RoundingMode;
+
+using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
+using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
+
+class LlvmLibcWcstodTest : public LIBC_NAMESPACE::testing::ErrnoCheckingTest,
+ ForceRoundingModeTest<RoundingMode::Nearest> {
+public:
+ void run_test(const wchar_t *inputString, const ptrdiff_t expectedStrLen,
+ const uint64_t expectedRawData, const int expectedErrno = 0) {
+ // expectedRawData is the expected double result as a uint64_t, organized
+ // according to IEEE754:
+ //
+ // +-- 1 Sign Bit +-- 52 Mantissa bits
+ // | |
+ // | +-------------------------+------------------------+
+ // | | |
+ // SEEEEEEEEEEEMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
+ // | |
+ // +----+----+
+ // |
+ // +-- 11 Exponent Bits
+ //
+ // This is so that the result can be compared in parts.
+ wchar_t *str_end = nullptr;
+
+ LIBC_NAMESPACE::fputil::FPBits<double> expected_fp =
+ LIBC_NAMESPACE::fputil::FPBits<double>(expectedRawData);
+
+ double result = LIBC_NAMESPACE::wcstod(inputString, &str_end);
+ if (expectedErrno == 0)
+ EXPECT_THAT(result, Succeeds<double>(expected_fp.get_val()));
+ else
+ EXPECT_THAT(result, Fails<double>(expectedErrno, expected_fp.get_val()));
+ EXPECT_EQ(str_end - inputString, expectedStrLen);
+ }
+};
+
+TEST_F(LlvmLibcWcstodTest, SimpleTest) {
+ run_test(L"123", 3, uint64_t(0x405ec00000000000));
+
+ // This should fail on Eisel-Lemire, forcing a fallback to simple decimal
+ // conversion.
+ run_test(L"12345678901234549760", 20, uint64_t(0x43e56a95319d63d8));
+
+ // Found while looking for difficult test cases here:
+ // https://github.com/nigeltao/parse-number-fxx-test-data/blob/main/more-test-cases/golang-org-issue-36657.txt
+ run_test(L"1090544144181609348835077142190", 31, uint64_t(0x462b8779f2474dfb));
+
+ run_test(L"0x123", 5, uint64_t(0x4072300000000000));
+}
+
+// These are tests that have caused problems in the past.
+TEST_F(LlvmLibcWcstodTest, SpecificFailures) {
+ run_test(L"3E70000000000000", 16, uint64_t(0x7FF0000000000000), ERANGE);
+ run_test(L"358416272e-33", 13, uint64_t(0x3adbbb2a68c9d0b9));
+ run_test(L"2.16656806400000023841857910156251e9", 36,
+ uint64_t(0x41e0246690000001));
+ run_test(L"27949676547093071875", 20, uint64_t(0x43f83e132bc608c9));
+ run_test(
+ L"10000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "0000000000e-800",
+ 806, 0x3ff0000000000000);
+ run_test(
+ L"10000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "0000000000e-799",
+ 806, 0x4024000000000000);
+ run_test(
+ L"10000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "00000000000e-800",
+ 807, 0x4024000000000000);
+ run_test(
+ L"10000000000000000000000000000000000000000000000000000000000000000e-64",
+ 69, 0x3ff0000000000000);
+ run_test(
+ L"10000000000000000000000000000000000000000000000000000000000000000000000"
+ "0000000000000000000000000000000000000000000000000000000000e-128",
+ 134, 0x3ff0000000000000);
+ run_test(L"100000000000000000000000000000000000000000000000000000000000000000"
+ "0000000000000000000000000000000000000000000000000000000000000000000"
+ "0000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000e-256",
+ 262, 0x3ff0000000000000);
+ run_test(L"100000000000000000000000000000000000000000000000000000000000000000"
+ "0000000000000000000000000000000000000000000000000000000000000000000"
+ "0000000000000000000000000000000000000000000000000000000000000000000"
+ "0000000000000000000000000000000000000000000000000000000000000000000"
+ "0000000000000000000000000000000000000000000000000000000000000000000"
+ "0000000000000000000000000000000000000000000000000000000000000000000"
+ "0000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000e-512",
+ 518, 0x3ff0000000000000);
+ run_test(
+ L"10000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000e-1024",
+ 1031, 0x3ff0000000000000);
+ run_test(
+ L"0"
+ "100000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "00000000000000000e-1024",
+ 1032, 0x3ff0000000000000);
+}
+
+TEST_F(LlvmLibcWcstodTest, FuzzFailures) {
+ run_test(L"-\xff\xff\xff\xff\xff\xff\xff\x01", 0, uint64_t(0));
+ run_test(L"-.????", 0, uint64_t(0));
+ run_test(
+ L"44444444444444444444444444444444444444444444444444A44444444444444444"
+ "44444444444*\x99\xff\xff\xff\xff",
+ 50, uint64_t(0x4a3e68fdd0e0b2d8));
+ run_test(L"-NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNKNNNNNNNNNNNNNNNNNN?"
+ "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN?",
+ 0, uint64_t(0));
+ run_test(L"0x.666E40", 9, uint64_t(0x3fd99b9000000000));
+
+ // glibc version 2.36 and higher (not tested with lower versions) disagrees
+ // with this result, but ours is correct for the nearest rounding mode. See
+ // this bug: https://sourceware.org/bugzilla/show_bug.cgi?id=30220
+ run_test(L"0x30000002222225p-1077", 22, uint64_t(0x0006000000444445), ERANGE);
+
+ // This value triggered a bug by having an exponent exactly equal to the
+ // maximum. The overflow checks would accept a value less than the max value
+ // as valid and greater than the max value as invalid (and set it to the max),
+ // but an exponent of exactly max value hit the else condition which is
+ // intended for underflow and set the exponent to the min exponent.
+ run_test(
+ L"18477446000000000000000000000000000005230000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000935166201543003765631683711878842"
+ "388777446000000000000430037600000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000005238581124701719460000000"
+ "000000000017194600000000000000000070046000000000000000000000000100000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000002000000000000000"
+ "000000000000056316837118788423887774460000000000000000000000000000052385"
+ "811247017194600000000000000000171946000000000000000000700460000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000002000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000523858112470171946000000"
+ "000000000001719460000000000000000007004600000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000"
+ "0200000000000000000E608",
+ 1462, uint64_t(0x7ff0000000000000), ERANGE);
+
+ // Same as above but for hex.
+ run_test(L"0x0164810157p2047", 17, uint64_t(0x7ff0000000000000), ERANGE);
+
+ // This test ensures that only the correct number of characters is accepted.
+ // An exponent symbol followed by a sign isn't a valid expon...
[truncated]
|
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
michaelrj-google
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
These are simply implemented as specializations of strtofloatingpoint for double / long double and for wchar_t. The unit tests are copied from the strtod / strtold ones.