From 6350b7a41e54774fe6f361d1202f99ba641b8834 Mon Sep 17 00:00:00 2001 From: Vinayak Dev 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 | 21 ++ 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, 650 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 8e1ab5cd65f00..8b1cd3fb10527 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 76010a4b4533a..3e58e3b886456 100644 --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -962,6 +962,8 @@ def StdC : StandardSpec<"stdc"> { FunctionSpec<"srand", RetValSpec, [ArgSpec]>, FunctionSpec<"strfromf", RetValSpec, [ArgSpec, ArgSpec, ArgSpec, ArgSpec]>, + FunctionSpec<"strfromd", RetValSpec, [ArgSpec, ArgSpec, ArgSpec, ArgSpec]>, + FunctionSpec<"strfroml", RetValSpec, [ArgSpec, ArgSpec, ArgSpec, ArgSpec]>, FunctionSpec<"strtof", RetValSpec, [ArgSpec, ArgSpec]>, FunctionSpec<"strtod", RetValSpec, [ArgSpec, ArgSpec]>, diff --git a/libc/src/stdlib/CMakeLists.txt b/libc/src/stdlib/CMakeLists.txt index 22f7f990fb08a..a2b16e73f3532 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 0000000000000..1d02a7ad11244 --- /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 +#include + +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(&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 0000000000000..d2c3fefb6300d --- /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 + +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 b551a58af05a3..492c2c33cf080 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 0000000000000..6b8781e7c04e1 --- /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 +#include + +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(&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 0000000000000..e99d035e4da60 --- /dev/null +++ b/libc/src/stdlib/strfroml.h @@ -0,0 +1,21 @@ +//===-- 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 + +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 cb42bc56f51c5..3ccc1cde91934 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 0000000000000..f695bbb335bdb --- /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(sizeof(expected_str) - 1)); \ + EXPECT_STREQ(actual_str, expected_str); + +template +class StrfromTest : public LIBC_NAMESPACE::testing::Test { + + static const bool is_single_prec = + LIBC_NAMESPACE::cpp::is_same::value; + static const bool is_double_prec = + LIBC_NAMESPACE::cpp::is_same::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; \ + 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 0000000000000..55724d7e902b5 --- /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 c5489f5f3af2a..8b987fd434ac5 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 0000000000000..cf472a39a5bf8 --- /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)