[libc-commits] [libc] [libc] Implement strfromd() and strfroml() (PR #86113)
Vinayak Dev via libc-commits
libc-commits at lists.llvm.org
Fri Mar 22 10:50:11 PDT 2024
https://github.com/vinayakdsci updated https://github.com/llvm/llvm-project/pull/86113
>From cde719901f66781148d6d3e85f7380fb2495830b Mon Sep 17 00:00:00 2001
From: Vinayak Dev <vinayakdev.sci at gmail.com>
Date: Thu, 21 Mar 2024 16:46:46 +0530
Subject: [PATCH] [libc] Implement strfromd() and strfroml()
---
libc/config/linux/x86_64/entrypoints.txt | 2 +
libc/spec/stdc.td | 2 +
libc/src/stdlib/CMakeLists.txt | 20 ++
libc/src/stdlib/strfromd.cpp | 42 +++
libc/src/stdlib/strfromd.h | 21 ++
libc/src/stdlib/strfromf.h | 2 +-
libc/src/stdlib/strfroml.cpp | 47 +++
libc/src/stdlib/strfroml.h | 22 ++
libc/test/src/stdlib/CMakeLists.txt | 31 ++
libc/test/src/stdlib/StrfromTest.h | 435 +++++++++++++++++++++++
libc/test/src/stdlib/strfromd_test.cpp | 13 +
libc/test/src/stdlib/strfromf_test.cpp | 98 +----
libc/test/src/stdlib/strfroml_test.cpp | 13 +
13 files changed, 651 insertions(+), 97 deletions(-)
create mode 100644 libc/src/stdlib/strfromd.cpp
create mode 100644 libc/src/stdlib/strfromd.h
create mode 100644 libc/src/stdlib/strfroml.cpp
create mode 100644 libc/src/stdlib/strfroml.h
create mode 100644 libc/test/src/stdlib/StrfromTest.h
create mode 100644 libc/test/src/stdlib/strfromd_test.cpp
create mode 100644 libc/test/src/stdlib/strfroml_test.cpp
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 8e1ab5cd65f005..8b1cd3fb105271 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -180,7 +180,9 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.stdlib.qsort_r
libc.src.stdlib.rand
libc.src.stdlib.srand
+ libc.src.stdlib.strfromd
libc.src.stdlib.strfromf
+ libc.src.stdlib.strfroml
libc.src.stdlib.strtod
libc.src.stdlib.strtof
libc.src.stdlib.strtol
diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td
index 76010a4b4533a7..3e58e3b8864566 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -962,6 +962,8 @@ def StdC : StandardSpec<"stdc"> {
FunctionSpec<"srand", RetValSpec<VoidType>, [ArgSpec<UnsignedIntType>]>,
FunctionSpec<"strfromf", RetValSpec<IntType>, [ArgSpec<CharRestrictedPtr>, ArgSpec<SizeTType>, ArgSpec<ConstCharRestrictedPtr>, ArgSpec<FloatType>]>,
+ FunctionSpec<"strfromd", RetValSpec<IntType>, [ArgSpec<CharRestrictedPtr>, ArgSpec<SizeTType>, ArgSpec<ConstCharRestrictedPtr>, ArgSpec<DoubleType>]>,
+ FunctionSpec<"strfroml", RetValSpec<IntType>, [ArgSpec<CharRestrictedPtr>, ArgSpec<SizeTType>, ArgSpec<ConstCharRestrictedPtr>, ArgSpec<LongDoubleType>]>,
FunctionSpec<"strtof", RetValSpec<FloatType>, [ArgSpec<ConstCharRestrictedPtr>, ArgSpec<CharRestrictedPtrPtr>]>,
FunctionSpec<"strtod", RetValSpec<DoubleType>, [ArgSpec<ConstCharRestrictedPtr>, ArgSpec<CharRestrictedPtrPtr>]>,
diff --git a/libc/src/stdlib/CMakeLists.txt b/libc/src/stdlib/CMakeLists.txt
index 22f7f990fb08a8..a2b16e73f35321 100644
--- a/libc/src/stdlib/CMakeLists.txt
+++ b/libc/src/stdlib/CMakeLists.txt
@@ -62,6 +62,26 @@ add_entrypoint_object(
.str_from_util
)
+add_entrypoint_object(
+ strfromd
+ SRCS
+ strfromd.cpp
+ HDRS
+ strfromd.h
+ DEPENDS
+ .str_from_util
+)
+
+add_entrypoint_object(
+ strfroml
+ SRCS
+ strfroml.cpp
+ HDRS
+ strfroml.h
+ DEPENDS
+ .str_from_util
+)
+
add_header_library(
str_from_util
HDRS
diff --git a/libc/src/stdlib/strfromd.cpp b/libc/src/stdlib/strfromd.cpp
new file mode 100644
index 00000000000000..1d02a7ad112442
--- /dev/null
+++ b/libc/src/stdlib/strfromd.cpp
@@ -0,0 +1,42 @@
+//===-- Implementation of strfromd ------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/stdlib/strfromd.h"
+#include "src/stdlib/str_from_util.h"
+
+#include <stdarg.h>
+#include <stddef.h>
+
+namespace LIBC_NAMESPACE {
+
+LLVM_LIBC_FUNCTION(int, strfromd,
+ (char *__restrict s, size_t n, const char *__restrict format,
+ double fp)) {
+ LIBC_ASSERT(s != nullptr);
+
+ printf_core::FormatSection section =
+ internal::parse_format_string(format, fp);
+ printf_core::WriteBuffer wb(s, (n > 0 ? n - 1 : 0));
+ printf_core::Writer writer(&wb);
+
+ int result = 0;
+ if (section.has_conv)
+ result = internal::strfromfloat_convert<double>(&writer, section);
+ else
+ result = writer.write(section.raw_string);
+
+ if (result < 0)
+ return result;
+
+ if (n > 0)
+ wb.buff[wb.buff_cur] = '\0';
+
+ return writer.get_chars_written();
+}
+
+} // namespace LIBC_NAMESPACE
diff --git a/libc/src/stdlib/strfromd.h b/libc/src/stdlib/strfromd.h
new file mode 100644
index 00000000000000..d2c3fefb6300dc
--- /dev/null
+++ b/libc/src/stdlib/strfromd.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for strfromd ------------------------*- 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_STDLIB_STRFROMD_H
+#define LLVM_LIBC_SRC_STDLIB_STRFROMD_H
+
+#include <stddef.h>
+
+namespace LIBC_NAMESPACE {
+
+int strfromd(char *__restrict s, size_t n, const char *__restrict format,
+ double fp);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_STDLIB_STRFROMD_H
diff --git a/libc/src/stdlib/strfromf.h b/libc/src/stdlib/strfromf.h
index b551a58af05a3e..492c2c33cf080b 100644
--- a/libc/src/stdlib/strfromf.h
+++ b/libc/src/stdlib/strfromf.h
@@ -18,4 +18,4 @@ int strfromf(char *__restrict s, size_t n, const char *__restrict format,
} // namespace LIBC_NAMESPACE
-#endif // LLVM_LIBC_SRC_STDLIB_STRTOF_H
+#endif // LLVM_LIBC_SRC_STDLIB_STRFROMF_H
diff --git a/libc/src/stdlib/strfroml.cpp b/libc/src/stdlib/strfroml.cpp
new file mode 100644
index 00000000000000..6b8781e7c04e11
--- /dev/null
+++ b/libc/src/stdlib/strfroml.cpp
@@ -0,0 +1,47 @@
+//===-- Implementation of strfroml ------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/stdlib/strfroml.h"
+#include "src/stdlib/str_from_util.h"
+
+#include <stdarg.h>
+#include <stddef.h>
+
+namespace LIBC_NAMESPACE {
+
+LLVM_LIBC_FUNCTION(int, strfroml,
+ (char *__restrict s, size_t n, const char *__restrict format,
+ long double fp)) {
+ LIBC_ASSERT(s != nullptr);
+
+ printf_core::FormatSection section =
+ internal::parse_format_string(format, fp);
+
+ // To ensure that the conversion function actually uses long double,
+ // the length modifier has to be set to LenghtModifier::L
+ section.length_modifier = printf_core::LengthModifier::L;
+
+ printf_core::WriteBuffer wb(s, (n > 0 ? n - 1 : 0));
+ printf_core::Writer writer(&wb);
+
+ int result = 0;
+ if (section.has_conv)
+ result = internal::strfromfloat_convert<long double>(&writer, section);
+ else
+ result = writer.write(section.raw_string);
+
+ if (result < 0)
+ return result;
+
+ if (n > 0)
+ wb.buff[wb.buff_cur] = '\0';
+
+ return writer.get_chars_written();
+}
+
+} // namespace LIBC_NAMESPACE
diff --git a/libc/src/stdlib/strfroml.h b/libc/src/stdlib/strfroml.h
new file mode 100644
index 00000000000000..05f939e187b885
--- /dev/null
+++ b/libc/src/stdlib/strfroml.h
@@ -0,0 +1,22 @@
+
+//===-- Implementation header for strfroml ------------------------*- 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_STDLIB_STRFROML_H
+#define LLVM_LIBC_SRC_STDLIB_STRFROML_H
+
+#include <stddef.h>
+
+namespace LIBC_NAMESPACE {
+
+int strfroml(char *__restrict s, size_t n, const char *__restrict format,
+ long double fp);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_STDLIB_STRFROML_H
diff --git a/libc/test/src/stdlib/CMakeLists.txt b/libc/test/src/stdlib/CMakeLists.txt
index cb42bc56f51c5f..3ccc1cde91934c 100644
--- a/libc/test/src/stdlib/CMakeLists.txt
+++ b/libc/test/src/stdlib/CMakeLists.txt
@@ -168,6 +168,14 @@ add_libc_test(
.strtol_test_support
)
+add_header_library(
+ strfrom_test_support
+ HDRS
+ StrfromTest.h
+ DEPENDS
+ libc.src.__support.CPP.type_traits
+)
+
add_libc_test(
strfromf_test
SUITE
@@ -175,9 +183,32 @@ add_libc_test(
SRCS
strfromf_test.cpp
DEPENDS
+ .strfrom_test_support
libc.src.stdlib.strfromf
)
+add_libc_test(
+ strfromd_test
+ SUITE
+ libc-stdlib-tests
+ SRCS
+ strfromd_test.cpp
+ DEPENDS
+ .strfrom_test_support
+ libc.src.stdlib.strfromd
+)
+
+add_libc_test(
+ strfroml_test
+ SUITE
+ libc-stdlib-tests
+ SRCS
+ strfroml_test.cpp
+ DEPENDS
+ .strfrom_test_support
+ libc.src.stdlib.strfroml
+)
+
add_libc_test(
abs_test
SUITE
diff --git a/libc/test/src/stdlib/StrfromTest.h b/libc/test/src/stdlib/StrfromTest.h
new file mode 100644
index 00000000000000..f695bbb335bdbe
--- /dev/null
+++ b/libc/test/src/stdlib/StrfromTest.h
@@ -0,0 +1,435 @@
+//===-- A template class for testing strfrom functions ----------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/__support/CPP/type_traits.h"
+#include "test/UnitTest/Test.h"
+
+#define ASSERT_STREQ_LEN(actual_written, actual_str, expected_str) \
+ EXPECT_EQ(actual_written, static_cast<int>(sizeof(expected_str) - 1)); \
+ EXPECT_STREQ(actual_str, expected_str);
+
+template <typename InputT>
+class StrfromTest : public LIBC_NAMESPACE::testing::Test {
+
+ static const bool is_single_prec =
+ LIBC_NAMESPACE::cpp::is_same<InputT, float>::value;
+ static const bool is_double_prec =
+ LIBC_NAMESPACE::cpp::is_same<InputT, double>::value;
+
+ using FunctionT = int (*)(char *, size_t, const char *, InputT fp);
+
+public:
+ void floatDecimalFormat(FunctionT func) {
+ if (is_single_prec)
+ floatDecimalSinglePrec(func);
+ else if (is_double_prec)
+ floatDecimalDoublePrec(func);
+ else
+ floatDecimalLongDoublePrec(func);
+ }
+
+ void floatHexExpFormat(FunctionT func) {
+ if (is_single_prec)
+ floatHexExpSinglePrec(func);
+ else if (is_double_prec)
+ floatHexExpDoublePrec(func);
+ else
+ floatHexExpLongDoublePrec(func);
+ }
+
+ void floatDecimalExpFormat(FunctionT func) {
+ if (is_single_prec)
+ floatDecimalExpSinglePrec(func);
+ else if (is_double_prec)
+ floatDecimalExpDoublePrec(func);
+ else
+ floatDecimalExpLongDoublePrec(func);
+ }
+
+ void floatDecimalAutoFormat(FunctionT func) {
+ if (is_single_prec)
+ floatDecimalAutoSinglePrec(func);
+ else if (is_double_prec)
+ floatDecimalAutoDoublePrec(func);
+ else
+ floatDecimalAutoLongDoublePrec(func);
+ }
+
+ void improperFormatString(FunctionT func) {
+ char buff[100];
+ int written;
+ const bool is_long_double = !is_single_prec && !is_double_prec;
+
+ written = func(buff, 37, "A simple string with no conversions.", 1.0);
+ ASSERT_STREQ_LEN(written, buff, "A simple string with no conversions.");
+
+ written =
+ func(buff, 37,
+ "%A simple string with one conversion, should overwrite.", 1.0);
+ ASSERT_STREQ_LEN(written, buff, is_long_double ? "0X8P-3" : "0X1P+0");
+
+ written = func(buff, 74,
+ "A simple string with one conversion in %A "
+ "between, writes string as it is",
+ 1.0);
+ ASSERT_STREQ_LEN(written, buff,
+ "A simple string with one conversion in %A between, "
+ "writes string as it is");
+
+ written = func(buff, 36, "A simple string with one conversion", 1.0);
+ ASSERT_STREQ_LEN(written, buff, "A simple string with one conversion");
+
+ written = func(buff, 20, "%1f", 1234567890.0);
+ ASSERT_STREQ_LEN(written, buff, "%1f");
+ }
+
+ void insufficentBufsize(FunctionT func) {
+ char buff[20];
+ int written;
+
+ written = func(buff, 5, "%f", 1234567890.0);
+ EXPECT_EQ(written, 17);
+ ASSERT_STREQ(buff, "1234");
+
+ written = func(buff, 5, "%.5f", 1.05);
+ EXPECT_EQ(written, 7);
+ ASSERT_STREQ(buff, "1.05");
+
+ written = func(buff, 0, "%g", 1.0);
+ EXPECT_EQ(written, 1);
+ ASSERT_STREQ(buff, "1.05"); // Make sure that buff has not changed
+ }
+
+ void floatDecimalSinglePrec(FunctionT func) {
+ char buff[70];
+ int written;
+
+ written = func(buff, 16, "%f", 1.0);
+ ASSERT_STREQ_LEN(written, buff, "1.000000");
+
+ written = func(buff, 20, "%f", 1234567890.0);
+ ASSERT_STREQ_LEN(written, buff, "1234567936.000000");
+
+ written = func(buff, 67, "%.3f", 1.0);
+ ASSERT_STREQ_LEN(written, buff, "1.000");
+ }
+
+ void floatDecimalDoublePrec(FunctionT func) {
+ char buff[500];
+ int written;
+
+ written = func(buff, 99, "%f", 1.0);
+ ASSERT_STREQ_LEN(written, buff, "1.000000");
+
+ written = func(buff, 99, "%F", -1.0);
+ ASSERT_STREQ_LEN(written, buff, "-1.000000");
+
+ written = func(buff, 99, "%f", -1.234567);
+ ASSERT_STREQ_LEN(written, buff, "-1.234567");
+
+ written = func(buff, 99, "%f", 0.0);
+ ASSERT_STREQ_LEN(written, buff, "0.000000");
+
+ written = func(buff, 99, "%f", 1.5);
+ ASSERT_STREQ_LEN(written, buff, "1.500000");
+
+ written = func(buff, 499, "%f", 1e300);
+ ASSERT_STREQ_LEN(written, buff,
+ "100000000000000005250476025520442024870446858110815915491"
+ "585411551180245"
+ "798890819578637137508044786404370444383288387817694252323"
+ "536043057564479"
+ "218478670698284838720092657580373783023379478809005936895"
+ "323497079994508"
+ "111903896764088007465274278014249457925878882005684283811"
+ "566947219638686"
+ "5459400540160.000000");
+
+ written = func(buff, 99, "%f", 0.1);
+ ASSERT_STREQ_LEN(written, buff, "0.100000");
+
+ written = func(buff, 99, "%f", 1234567890123456789.0);
+ ASSERT_STREQ_LEN(written, buff, "1234567890123456768.000000");
+
+ written = func(buff, 99, "%f", 9999999999999.99);
+ ASSERT_STREQ_LEN(written, buff, "9999999999999.990234");
+
+ written = func(buff, 99, "%f", 0.1);
+ ASSERT_STREQ_LEN(written, buff, "0.100000");
+
+ written = func(buff, 99, "%f", 1234567890123456789.0);
+ ASSERT_STREQ_LEN(written, buff, "1234567890123456768.000000");
+
+ written = func(buff, 99, "%f", 9999999999999.99);
+ ASSERT_STREQ_LEN(written, buff, "9999999999999.990234");
+
+ // Precision Tests
+ written = func(buff, 100, "%.2f", 9999999999999.99);
+ ASSERT_STREQ_LEN(written, buff, "9999999999999.99");
+
+ written = func(buff, 100, "%.1f", 9999999999999.99);
+ ASSERT_STREQ_LEN(written, buff, "10000000000000.0");
+
+ written = func(buff, 100, "%.5f", 1.25);
+ ASSERT_STREQ_LEN(written, buff, "1.25000");
+
+ written = func(buff, 100, "%.0f", 1.25);
+ ASSERT_STREQ_LEN(written, buff, "1");
+
+ written = func(buff, 100, "%.20f", 1.234e-10);
+ ASSERT_STREQ_LEN(written, buff, "0.00000000012340000000");
+ }
+
+ void floatDecimalLongDoublePrec(FunctionT func) {
+ char buff[45];
+ int written;
+
+ written = func(buff, 40, "%f", 1.0L);
+ ASSERT_STREQ_LEN(written, buff, "1.000000");
+
+ written = func(buff, 10, "%.f", -2.5L);
+ ASSERT_STREQ_LEN(written, buff, "-2");
+ }
+
+ void floatHexExpSinglePrec(FunctionT func) {
+ char buff[25];
+ int written;
+
+ written = func(buff, 0, "%a", 1234567890.0);
+ EXPECT_EQ(written, 14);
+
+ written = func(buff, 20, "%a", 1234567890.0);
+ EXPECT_EQ(written, 14);
+ ASSERT_STREQ(buff, "0x1.26580cp+30");
+
+ written = func(buff, 20, "%A", 1234567890.0);
+ EXPECT_EQ(written, 14);
+ ASSERT_STREQ(buff, "0X1.26580CP+30");
+ }
+
+ void floatHexExpDoublePrec(FunctionT func) {
+ char buff[60];
+ int written;
+
+ written = func(buff, 10, "%a", 1.0);
+ ASSERT_STREQ_LEN(written, buff, "0x1p+0");
+
+ written = func(buff, 10, "%A", -1.0);
+ ASSERT_STREQ_LEN(written, buff, "-0X1P+0");
+
+ written = func(buff, 30, "%a", -0x1.abcdef12345p0);
+ ASSERT_STREQ_LEN(written, buff, "-0x1.abcdef12345p+0");
+
+ written = func(buff, 50, "%A", 0x1.abcdef12345p0);
+ ASSERT_STREQ_LEN(written, buff, "0X1.ABCDEF12345P+0");
+
+ written = func(buff, 10, "%a", 0.0);
+ ASSERT_STREQ_LEN(written, buff, "0x0p+0");
+
+ written = func(buff, 40, "%a", 1.0e100);
+ ASSERT_STREQ_LEN(written, buff, "0x1.249ad2594c37dp+332");
+
+ written = func(buff, 30, "%a", 0.1);
+ ASSERT_STREQ_LEN(written, buff, "0x1.999999999999ap-4");
+ }
+
+ void floatHexExpLongDoublePrec(FunctionT func) {
+ char buff[55];
+ int written;
+
+ written = func(buff, 50, "%a", 0.1L);
+#if defined(LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80)
+ ASSERT_STREQ_LEN(written, buff, "0xc.ccccccccccccccdp-7");
+#elif defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT64)
+ ASSERT_STREQ_LEN(written, buff, "0x1.999999999999ap-4");
+#elif defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT128)
+ ASSERT_STREQ_LEN(written, buff, "0x1.999999999999999999999999999ap-4");
+#endif
+
+ written = func(buff, 20, "%.1a", 0.1L);
+#if defined(LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80)
+ ASSERT_STREQ_LEN(written, buff, "0xc.dp-7");
+#elif defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT64)
+ ASSERT_STREQ_LEN(written, buff, "0x1.ap-4");
+#elif defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT128)
+ ASSERT_STREQ_LEN(written, buff, "0x1.ap-4");
+#endif
+
+ written = func(buff, 50, "%a", 1.0e1000L);
+#if defined(LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80)
+ ASSERT_STREQ_LEN(written, buff, "0xf.38db1f9dd3dac05p+3318");
+#elif defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT64)
+ ASSERT_STREQ_LEN(written, buff, "inf");
+#elif defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT128)
+ ASSERT_STREQ_LEN(written, buff, "0x1.e71b63f3ba7b580af1a52d2a7379p+3321");
+#endif
+
+ written = func(buff, 50, "%a", 1.0e-1000L);
+#if defined(LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80)
+ ASSERT_STREQ_LEN(written, buff, "0x8.68a9188a89e1467p-3325");
+#elif defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT64)
+ ASSERT_STREQ_LEN(written, buff, "0x0p+0");
+#elif defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT128)
+ ASSERT_STREQ_LEN(written, buff, "0x1.0d152311513c28ce202627c06ec2p-3322");
+#endif
+
+ written = func(buff, 50, "%.1a", 0xf.fffffffffffffffp16380L);
+#if defined(LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80)
+ ASSERT_STREQ_LEN(written, buff, "0x1.0p+16384");
+#elif defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT64)
+ ASSERT_STREQ_LEN(written, buff, "inf");
+#elif defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT128)
+ ASSERT_STREQ_LEN(written, buff, "0x2.0p+16383");
+#endif
+ }
+
+ void floatDecimalExpSinglePrec(FunctionT func) {
+ char buff[25];
+ int written;
+
+ written = func(buff, 20, "%.9e", 1234567890.0);
+ ASSERT_STREQ_LEN(written, buff, "1.234567936e+09");
+
+ written = func(buff, 20, "%.9E", 1234567890.0);
+ ASSERT_STREQ_LEN(written, buff, "1.234567936E+09");
+ }
+
+ void floatDecimalExpDoublePrec(FunctionT func) {
+ char buff[101];
+ int written;
+
+ written = func(buff, 100, "%e", 1.0);
+ ASSERT_STREQ_LEN(written, buff, "1.000000e+00");
+
+ written = func(buff, 100, "%E", -1.0);
+ ASSERT_STREQ_LEN(written, buff, "-1.000000E+00");
+
+ written = func(buff, 100, "%e", -1.234567);
+ ASSERT_STREQ_LEN(written, buff, "-1.234567e+00");
+
+ written = func(buff, 100, "%e", 0.0);
+ ASSERT_STREQ_LEN(written, buff, "0.000000e+00");
+
+ written = func(buff, 100, "%e", 1.5);
+ ASSERT_STREQ_LEN(written, buff, "1.500000e+00");
+
+ written = func(buff, 100, "%e", 1e300);
+ ASSERT_STREQ_LEN(written, buff, "1.000000e+300");
+
+ written = func(buff, 100, "%e", 1234567890123456789.0);
+ ASSERT_STREQ_LEN(written, buff, "1.234568e+18");
+
+ // Precision Tests
+ written = func(buff, 100, "%.1e", 1.0);
+ ASSERT_STREQ_LEN(written, buff, "1.0e+00");
+
+ written = func(buff, 100, "%.1e", 1.99);
+ ASSERT_STREQ_LEN(written, buff, "2.0e+00");
+
+ written = func(buff, 100, "%.1e", 9.99);
+ ASSERT_STREQ_LEN(written, buff, "1.0e+01");
+ }
+
+ void floatDecimalExpLongDoublePrec(FunctionT func) {
+ char buff[100];
+ int written;
+
+#if defined(LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80)
+ written = func(buff, 90, "%.9e", 1000000000500000000.1L);
+ ASSERT_STREQ_LEN(written, buff, "1.000000001e+18");
+
+ written = func(buff, 90, "%.9e", 1000000000500000000.0L);
+ ASSERT_STREQ_LEN(written, buff, "1.000000000e+18");
+
+ written = func(buff, 90, "%e", 0xf.fffffffffffffffp+16380L);
+ ASSERT_STREQ_LEN(written, buff, "1.189731e+4932");
+#endif // LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80
+ }
+
+ void floatDecimalAutoSinglePrec(FunctionT func) {
+ char buff[25];
+ int written;
+
+ written = func(buff, 20, "%.9g", 1234567890.0);
+ ASSERT_STREQ_LEN(written, buff, "1.23456794e+09");
+
+ written = func(buff, 20, "%.9G", 1234567890.0);
+ ASSERT_STREQ_LEN(written, buff, "1.23456794E+09");
+ }
+
+ void floatDecimalAutoDoublePrec(FunctionT func) {
+ char buff[120];
+ int written;
+
+ written = func(buff, 100, "%g", 1234567890123456789.0);
+ ASSERT_STREQ_LEN(written, buff, "1.23457e+18");
+
+ written = func(buff, 100, "%g", 9999990000000.00);
+ ASSERT_STREQ_LEN(written, buff, "9.99999e+12");
+
+ written = func(buff, 100, "%g", 9999999000000.00);
+ ASSERT_STREQ_LEN(written, buff, "1e+13");
+
+ written = func(buff, 100, "%g", 0xa.aaaaaaaaaaaaaabp-7);
+ ASSERT_STREQ_LEN(written, buff, "0.0833333");
+
+ written = func(buff, 100, "%g", 0.00001);
+ ASSERT_STREQ_LEN(written, buff, "1e-05");
+
+ // Precision Tests
+ written = func(buff, 100, "%.0g", 0.0);
+ ASSERT_STREQ_LEN(written, buff, "0");
+
+ written = func(buff, 100, "%.2g", 0.1);
+ ASSERT_STREQ_LEN(written, buff, "0.1");
+
+ written = func(buff, 100, "%.2g", 1.09);
+ ASSERT_STREQ_LEN(written, buff, "1.1");
+
+ written = func(buff, 100, "%.15g", 22.25);
+ ASSERT_STREQ_LEN(written, buff, "22.25");
+
+ written = func(buff, 100, "%.20g", 1.234e-10);
+ ASSERT_STREQ_LEN(written, buff, "1.2340000000000000814e-10");
+ }
+
+ void floatDecimalAutoLongDoublePrec(FunctionT func) {
+ char buff[100];
+ int written;
+
+#if defined(LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80)
+ written = func(buff, 99, "%g", 0xf.fffffffffffffffp+16380L);
+ ASSERT_STREQ_LEN(written, buff, "1.18973e+4932");
+
+ written = func(buff, 99, "%g", 0xa.aaaaaaaaaaaaaabp-7L);
+ ASSERT_STREQ_LEN(written, buff, "0.0833333");
+
+ written = func(buff, 99, "%g", 9.99999999999e-100L);
+ ASSERT_STREQ_LEN(written, buff, "1e-99");
+#endif // LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80
+ }
+};
+
+#define STRFROM_TEST(InputType, name, func) \
+ using LlvmLibc##name##Test = StrfromTest<InputType>; \
+ TEST_F(LlvmLibc##name##Test, FloatDecimalFormat) { \
+ floatDecimalFormat(func); \
+ } \
+ TEST_F(LlvmLibc##name##Test, FloatHexExpFormat) { floatHexExpFormat(func); } \
+ TEST_F(LlvmLibc##name##Test, FloatDecimalAutoFormat) { \
+ floatDecimalAutoFormat(func); \
+ } \
+ TEST_F(LlvmLibc##name##Test, FloatDecimalExpFormat) { \
+ floatDecimalExpFormat(func); \
+ } \
+ TEST_F(LlvmLibc##name##Test, ImproperFormatString) { \
+ improperFormatString(func); \
+ } \
+ TEST_F(LlvmLibc##name##Test, InsufficientBufferSize) { \
+ insufficentBufsize(func); \
+ }
diff --git a/libc/test/src/stdlib/strfromd_test.cpp b/libc/test/src/stdlib/strfromd_test.cpp
new file mode 100644
index 00000000000000..55724d7e902b5c
--- /dev/null
+++ b/libc/test/src/stdlib/strfromd_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for strfromd --------------------------------------------===//
+//
+// 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 "StrfromTest.h"
+#include "src/stdlib/strfromd.h"
+#include "test/UnitTest/Test.h"
+
+STRFROM_TEST(double, Strfromd, LIBC_NAMESPACE::strfromd)
diff --git a/libc/test/src/stdlib/strfromf_test.cpp b/libc/test/src/stdlib/strfromf_test.cpp
index c5489f5f3af2ac..8b987fd434ac54 100644
--- a/libc/test/src/stdlib/strfromf_test.cpp
+++ b/libc/test/src/stdlib/strfromf_test.cpp
@@ -6,102 +6,8 @@
//
//===----------------------------------------------------------------------===//
+#include "StrfromTest.h"
#include "src/stdlib/strfromf.h"
#include "test/UnitTest/Test.h"
-TEST(LlvmLibcStrfromfTest, DecimalFloatFormat) {
- char buff[100];
- int written;
-
- written = LIBC_NAMESPACE::strfromf(buff, 16, "%f", 1.0);
- EXPECT_EQ(written, 8);
- ASSERT_STREQ(buff, "1.000000");
-
- written = LIBC_NAMESPACE::strfromf(buff, 20, "%f", 1234567890.0);
- EXPECT_EQ(written, 17);
- ASSERT_STREQ(buff, "1234567936.000000");
-
- written = LIBC_NAMESPACE::strfromf(buff, 5, "%f", 1234567890.0);
- EXPECT_EQ(written, 17);
- ASSERT_STREQ(buff, "1234");
-
- written = LIBC_NAMESPACE::strfromf(buff, 67, "%.3f", 1.0);
- EXPECT_EQ(written, 5);
- ASSERT_STREQ(buff, "1.000");
-
- written = LIBC_NAMESPACE::strfromf(buff, 20, "%1f", 1234567890.0);
- EXPECT_EQ(written, 3);
- ASSERT_STREQ(buff, "%1f");
-}
-
-TEST(LlvmLibcStrfromfTest, HexExpFloatFormat) {
- char buff[100];
- int written;
-
- written = LIBC_NAMESPACE::strfromf(buff, 0, "%a", 1234567890.0);
- EXPECT_EQ(written, 14);
-
- written = LIBC_NAMESPACE::strfromf(buff, 20, "%a", 1234567890.0);
- EXPECT_EQ(written, 14);
- ASSERT_STREQ(buff, "0x1.26580cp+30");
-
- written = LIBC_NAMESPACE::strfromf(buff, 20, "%A", 1234567890.0);
- EXPECT_EQ(written, 14);
- ASSERT_STREQ(buff, "0X1.26580CP+30");
-}
-
-TEST(LlvmLibcStrfromfTest, DecimalExpFloatFormat) {
- char buff[100];
- int written;
- written = LIBC_NAMESPACE::strfromf(buff, 20, "%.9e", 1234567890.0);
- EXPECT_EQ(written, 15);
- ASSERT_STREQ(buff, "1.234567936e+09");
-
- written = LIBC_NAMESPACE::strfromf(buff, 20, "%.9E", 1234567890.0);
- EXPECT_EQ(written, 15);
- ASSERT_STREQ(buff, "1.234567936E+09");
-}
-
-TEST(LlvmLibcStrfromfTest, AutoDecimalFloatFormat) {
- char buff[100];
- int written;
-
- written = LIBC_NAMESPACE::strfromf(buff, 20, "%.9g", 1234567890.0);
- EXPECT_EQ(written, 14);
- ASSERT_STREQ(buff, "1.23456794e+09");
-
- written = LIBC_NAMESPACE::strfromf(buff, 20, "%.9G", 1234567890.0);
- EXPECT_EQ(written, 14);
- ASSERT_STREQ(buff, "1.23456794E+09");
-
- written = LIBC_NAMESPACE::strfromf(buff, 0, "%G", 1.0);
- EXPECT_EQ(written, 1);
-}
-
-TEST(LlvmLibcStrfromfTest, ImproperFormatString) {
-
- char buff[100];
- int retval;
- retval = LIBC_NAMESPACE::strfromf(
- buff, 37, "A simple string with no conversions.", 1.0);
- EXPECT_EQ(retval, 36);
- ASSERT_STREQ(buff, "A simple string with no conversions.");
-
- retval = LIBC_NAMESPACE::strfromf(
- buff, 37, "%A simple string with one conversion, should overwrite.", 1.0);
- EXPECT_EQ(retval, 6);
- ASSERT_STREQ(buff, "0X1P+0");
-
- retval = LIBC_NAMESPACE::strfromf(buff, 74,
- "A simple string with one conversion in %A "
- "between, writes string as it is",
- 1.0);
- EXPECT_EQ(retval, 73);
- ASSERT_STREQ(buff, "A simple string with one conversion in %A between, "
- "writes string as it is");
-
- retval = LIBC_NAMESPACE::strfromf(buff, 36,
- "A simple string with one conversion", 1.0);
- EXPECT_EQ(retval, 35);
- ASSERT_STREQ(buff, "A simple string with one conversion");
-}
+STRFROM_TEST(float, StrFromf, LIBC_NAMESPACE::strfromf)
diff --git a/libc/test/src/stdlib/strfroml_test.cpp b/libc/test/src/stdlib/strfroml_test.cpp
new file mode 100644
index 00000000000000..cf472a39a5bf88
--- /dev/null
+++ b/libc/test/src/stdlib/strfroml_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for strfroml --------------------------------------------===//
+//
+// 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 "StrfromTest.h"
+#include "src/stdlib/strfroml.h"
+#include "test/UnitTest/Test.h"
+
+STRFROM_TEST(long double, Strfroml, LIBC_NAMESPACE::strfroml)
More information about the libc-commits
mailing list