Merge pull request #236 from fastfloat/issue235

Verify and fix issue 235
This commit is contained in:
Daniel Lemire 2024-01-28 11:43:35 -05:00 committed by GitHub
commit a642af5235
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 177 additions and 138 deletions

16
.github/workflows/ubuntu22-sanitize.yml vendored Normal file
View File

@ -0,0 +1,16 @@
name: Ubuntu 22.04 CI Sanitized (GCC 11)
on: [push, pull_request]
jobs:
ubuntu-build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- name: Use cmake
run: |
mkdir build &&
cd build &&
cmake -DFASTFLOAT_TEST=ON -D FASTFLOAT_SANITIZE=ON .. &&
cmake --build . &&
ctest --output-on-failure

View File

@ -27,8 +27,12 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
auto answer = auto answer =
fast_float::from_chars(input_d.data(), input_d.data() + input_d.size(), result_d, format); fast_float::from_chars(input_d.data(), input_d.data() + input_d.size(), result_d, format);
std::string input_f = fdp.ConsumeRandomLengthString(128); std::string input_f = fdp.ConsumeRandomLengthString(128);
double result_f = 0.0; float result_f = 0.0;
answer = answer =
fast_float::from_chars(input_f.data(), input_f.data() + input_f.size(), result_f, format); fast_float::from_chars(input_f.data(), input_f.data() + input_f.size(), result_f, format);
int result_i = 0;
std::string input_i = fdp.ConsumeRandomLengthString(128);
answer =
fast_float::from_chars(input_i.data(), input_i.data() + input_i.size(), result_i);
return 0; return 0;
} }

View File

@ -442,8 +442,7 @@ parsed_number_string_t<UC> parse_number_string(UC const *p, UC const * pend, par
template <typename T, typename UC> template <typename T, typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20
from_chars_result_t<UC> parse_int_string(UC const* p, UC const* pend, T& value, int base) from_chars_result_t<UC> parse_int_string(UC const* p, UC const* pend, T& value, int base) {
{
from_chars_result_t<UC> answer; from_chars_result_t<UC> answer;
UC const* const first = p; UC const* const first = p;
@ -463,9 +462,11 @@ from_chars_result_t<UC> parse_int_string(UC const* p, UC const* pend, T& value,
} }
UC const* const start_num = p; UC const* const start_num = p;
while (*p == UC('0')) {
while (p!= pend && *p == UC('0')) {
++p; ++p;
} }
const bool has_leading_zeros = p > start_num; const bool has_leading_zeros = p > start_num;
UC const* const start_digits = p; UC const* const start_digits = p;

View File

@ -232,8 +232,7 @@ from_chars_result_t<UC> from_chars_advanced(UC const * first, UC const * last,
template <typename T, typename UC, typename> template <typename T, typename UC, typename>
FASTFLOAT_CONSTEXPR20 FASTFLOAT_CONSTEXPR20
from_chars_result_t<UC> from_chars(UC const* first, UC const* last, T& value, int base) noexcept from_chars_result_t<UC> from_chars(UC const* first, UC const* last, T& value, int base) noexcept {
{
static_assert (is_supported_char_type<UC>(), "only char, wchar_t, char16_t and char32_t are supported"); static_assert (is_supported_char_type<UC>(), "only char, wchar_t, char16_t and char32_t are supported");
from_chars_result_t<UC> answer; from_chars_result_t<UC> answer;

View File

@ -10,7 +10,7 @@ option(FASTFLOAT_SUPPLEMENTAL_TESTS "Run supplemental tests" ON)
if (NOT SYSTEM_DOCTEST) if (NOT SYSTEM_DOCTEST)
FetchContent_Declare(doctest FetchContent_Declare(doctest
GIT_REPOSITORY https://github.com/onqtam/doctest.git GIT_REPOSITORY https://github.com/onqtam/doctest.git
GIT_TAG v2.4.10) GIT_TAG v2.4.11)
endif() endif()
if (FASTFLOAT_SUPPLEMENTAL_TESTS) if (FASTFLOAT_SUPPLEMENTAL_TESTS)
FetchContent_Declare(supplemental_test_files FetchContent_Declare(supplemental_test_files
@ -81,6 +81,7 @@ fast_float_add_cpp_test(long_test)
fast_float_add_cpp_test(powersoffive_hardround) fast_float_add_cpp_test(powersoffive_hardround)
fast_float_add_cpp_test(string_test) fast_float_add_cpp_test(string_test)
fast_float_add_cpp_test(fast_int) fast_float_add_cpp_test(fast_int)
target_compile_features(fast_int PRIVATE cxx_std_17)
fast_float_add_cpp_test(json_fmt) fast_float_add_cpp_test(json_fmt)
fast_float_add_cpp_test(fortran) fast_float_add_cpp_test(fortran)

View File

@ -1,11 +1,33 @@
#ifndef __cplusplus
#error fastfloat requires a C++ compiler
#endif
// We want to enable the tests only for C++17 and above.
#ifndef FASTFLOAT_CPLUSPLUS
#if defined(_MSVC_LANG) && !defined(__clang__)
#define FASTFLOAT_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG)
#else
#define FASTFLOAT_CPLUSPLUS __cplusplus
#endif
#endif
#if FASTFLOAT_CPLUSPLUS >= 201703L
#include <cstdlib> #include <cstdlib>
#include <iostream> #include <iostream>
#include <vector> #include <vector>
#include <iomanip> #include <string_view>
#include <cstring> #include <cstring>
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
#include <cstdint> #include <cstdint>
template <class T>
std::string quoted(T& s) {
return "\""+std::string(s)+"\"";
}
/* /*
all tests conducted are to check fast_float::from_chars functionality with int and unsigned all tests conducted are to check fast_float::from_chars functionality with int and unsigned
test cases include: test cases include:
@ -26,414 +48,391 @@ within range base tests - max/min numbers are still within int/unsigned bit size
leading zeros tests - ignores all zeroes in front of valid number after converted from base leading zeros tests - ignores all zeroes in front of valid number after converted from base
*/ */
int main() int main() {
{
// int basic test // int basic test
const std::vector<int> int_basic_test_expected { 0, 10, -40, 1001, 9 }; const std::vector<int> int_basic_test_expected { 0, 10, -40, 1001, 9 };
const std::vector<std::string> int_basic_test { "0", "10 ", "-40", "1001 with text", "9.999" }; const std::vector<std::string_view> int_basic_test { "0", "10 ", "-40", "1001 with text", "9.999" };
for (std::size_t i = 0; i < int_basic_test.size(); ++i) for (std::size_t i = 0; i < int_basic_test.size(); ++i) {
{
const auto& f = int_basic_test[i]; const auto& f = int_basic_test[i];
int result; int result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc()) { if (answer.ec != std::errc()) {
if (answer.ec == std::errc::invalid_argument) { if (answer.ec == std::errc::invalid_argument) {
std::cerr << "could not convert to int for input: " << std::quoted(f) << " because of invalid arguement" << std::endl; std::cerr << "could not convert to int for input: " << quoted(f) << " because of invalid arguement" << std::endl;
} }
else if (answer.ec == std::errc::result_out_of_range) { else if (answer.ec == std::errc::result_out_of_range) {
std::cerr << "could not convert to int for input: " << std::quoted(f) << " because it's out of range" << std::endl; std::cerr << "could not convert to int for input: " << quoted(f) << " because it's out of range" << std::endl;
} }
else { else {
std::cerr << "could not convert to int for input: " << std::quoted(f) << " because of an unknown error" << std::endl; std::cerr << "could not convert to int for input: " << quoted(f) << " because of an unknown error" << std::endl;
} }
return EXIT_FAILURE; return EXIT_FAILURE;
} }
else if (result != int_basic_test_expected[i]) { else if (result != int_basic_test_expected[i]) {
std::cerr << "result " << std::quoted(f) << " did not match with expected int: " << int_basic_test_expected[i] << std::endl; std::cerr << "result " << quoted(f) << " did not match with expected int: " << int_basic_test_expected[i] << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// unsigned basic test // unsigned basic test
const std::vector<unsigned> unsigned_basic_test_expected { 0, 10, 1001, 9 }; const std::vector<unsigned> unsigned_basic_test_expected { 0, 10, 1001, 9 };
const std::vector<std::string> unsigned_basic_test { "0", "10 ", "1001 with text", "9.999" }; const std::vector<std::string_view> unsigned_basic_test { "0", "10 ", "1001 with text", "9.999" };
for (std::size_t i = 0; i < unsigned_basic_test.size(); ++i) for (std::size_t i = 0; i < unsigned_basic_test.size(); ++i) {
{
const auto& f = unsigned_basic_test[i]; const auto& f = unsigned_basic_test[i];
unsigned result; unsigned result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc()) { if (answer.ec != std::errc()) {
std::cerr << "could not convert to unsigned for input: " << std::quoted(f) << std::endl; std::cerr << "could not convert to unsigned for input: " << quoted(f) << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
else if (result != unsigned_basic_test_expected[i]) { else if (result != unsigned_basic_test_expected[i]) {
std::cerr << "result " << std::quoted(f) << " did not match with expected unsigned: " << unsigned_basic_test_expected[i] << std::endl; std::cerr << "result " << quoted(f) << " did not match with expected unsigned: " << unsigned_basic_test_expected[i] << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// int invalid error test // int invalid error test
const std::vector<std::string> int_invalid_argument_test{ "text", "text with 1002", "+50", " 50" }; const std::vector<std::string_view> int_invalid_argument_test{ "text", "text with 1002", "+50", " 50" };
for (std::size_t i = 0; i < int_invalid_argument_test.size(); ++i) for (std::size_t i = 0; i < int_invalid_argument_test.size(); ++i) {
{
const auto& f = int_invalid_argument_test[i]; const auto& f = int_invalid_argument_test[i];
int result; int result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc::invalid_argument) { if (answer.ec != std::errc::invalid_argument) {
std::cerr << "expected error should be 'invalid_argument' for: " << std::quoted(f) << std::endl; std::cerr << "expected error should be 'invalid_argument' for: " << quoted(f) << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// unsigned invalid error test // unsigned invalid error test
const std::vector<std::string> unsigned_invalid_argument_test{ "text", "text with 1002", "+50", " 50", "-50" }; const std::vector<std::string_view> unsigned_invalid_argument_test{ "text", "text with 1002", "+50", " 50", "-50" };
for (std::size_t i = 0; i < unsigned_invalid_argument_test.size(); ++i) for (std::size_t i = 0; i < unsigned_invalid_argument_test.size(); ++i) {
{
const auto& f = unsigned_invalid_argument_test[i]; const auto& f = unsigned_invalid_argument_test[i];
unsigned result; unsigned result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc::invalid_argument) { if (answer.ec != std::errc::invalid_argument) {
std::cerr << "expected error should be 'invalid_argument' for: " << std::quoted(f) << std::endl; std::cerr << "expected error should be 'invalid_argument' for: " << quoted(f) << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// int out of range error test #1 (8 bit) // int out of range error test #1 (8 bit)
const std::vector<std::string> int_out_of_range_test_1{ "2000000000000000000000", "128", "-129"}; const std::vector<std::string_view> int_out_of_range_test_1{ "2000000000000000000000", "128", "-129"};
for (std::size_t i = 0; i < int_out_of_range_test_1.size(); ++i) for (std::size_t i = 0; i < int_out_of_range_test_1.size(); ++i) {
{
const auto& f = int_out_of_range_test_1[i]; const auto& f = int_out_of_range_test_1[i];
int8_t result; int8_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc::result_out_of_range) { if (answer.ec != std::errc::result_out_of_range) {
std::cerr << "expected error for should be 'result_out_of_range': " << std::quoted(f) << std::endl; std::cerr << "expected error for should be 'result_out_of_range': " << quoted(f) << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// int out of range error test #2 (16 bit) // int out of range error test #2 (16 bit)
const std::vector<std::string> int_out_of_range_test_2{ "2000000000000000000000", "32768", "-32769"}; const std::vector<std::string_view> int_out_of_range_test_2{ "2000000000000000000000", "32768", "-32769"};
for (std::size_t i = 0; i < int_out_of_range_test_2.size(); ++i) for (std::size_t i = 0; i < int_out_of_range_test_2.size(); ++i) {
{
const auto& f = int_out_of_range_test_2[i]; const auto& f = int_out_of_range_test_2[i];
int16_t result; int16_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc::result_out_of_range) { if (answer.ec != std::errc::result_out_of_range) {
std::cerr << "expected error for should be 'result_out_of_range': " << std::quoted(f) << std::endl; std::cerr << "expected error for should be 'result_out_of_range': " << quoted(f) << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// int out of range error test #3 (32 bit) // int out of range error test #3 (32 bit)
const std::vector<std::string> int_out_of_range_test_3{ "2000000000000000000000", "2147483648", "-2147483649"}; const std::vector<std::string_view> int_out_of_range_test_3{ "2000000000000000000000", "2147483648", "-2147483649"};
for (std::size_t i = 0; i < int_out_of_range_test_3.size(); ++i) for (std::size_t i = 0; i < int_out_of_range_test_3.size(); ++i) {
{
const auto& f = int_out_of_range_test_3[i]; const auto& f = int_out_of_range_test_3[i];
int32_t result; int32_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc::result_out_of_range) { if (answer.ec != std::errc::result_out_of_range) {
std::cerr << "expected error for should be 'result_out_of_range': " << std::quoted(f) << std::endl; std::cerr << "expected error for should be 'result_out_of_range': " << quoted(f) << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// int out of range error test #4 (64 bit) // int out of range error test #4 (64 bit)
const std::vector<std::string> int_out_of_range_test_4{ "2000000000000000000000", "9223372036854775808", "-9223372036854775809"}; const std::vector<std::string_view> int_out_of_range_test_4{ "2000000000000000000000", "9223372036854775808", "-9223372036854775809"};
for (std::size_t i = 0; i < int_out_of_range_test_4.size(); ++i) for (std::size_t i = 0; i < int_out_of_range_test_4.size(); ++i) {
{
const auto& f = int_out_of_range_test_4[i]; const auto& f = int_out_of_range_test_4[i];
int64_t result; int64_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc::result_out_of_range) { if (answer.ec != std::errc::result_out_of_range) {
std::cerr << "expected error for should be 'result_out_of_range': " << std::quoted(f) << std::endl; std::cerr << "expected error for should be 'result_out_of_range': " << quoted(f) << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// unsigned out of range error test #1 (8 bit) // unsigned out of range error test #1 (8 bit)
const std::vector<std::string> unsigned_out_of_range_test_1{ "2000000000000000000000", "256" }; const std::vector<std::string_view> unsigned_out_of_range_test_1{ "2000000000000000000000", "256" };
for (std::size_t i = 0; i < unsigned_out_of_range_test_1.size(); ++i) for (std::size_t i = 0; i < unsigned_out_of_range_test_1.size(); ++i) {
{
const auto& f = unsigned_out_of_range_test_1[i]; const auto& f = unsigned_out_of_range_test_1[i];
uint8_t result; uint8_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc::result_out_of_range) { if (answer.ec != std::errc::result_out_of_range) {
std::cerr << "expected error for should be 'result_out_of_range': " << std::quoted(f) << std::endl; std::cerr << "expected error for should be 'result_out_of_range': " << quoted(f) << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// unsigned out of range error test #2 (16 bit) // unsigned out of range error test #2 (16 bit)
const std::vector<std::string> unsigned_out_of_range_test_2{ "2000000000000000000000", "65536" }; const std::vector<std::string_view> unsigned_out_of_range_test_2{ "2000000000000000000000", "65536" };
for (std::size_t i = 0; i < unsigned_out_of_range_test_2.size(); ++i) for (std::size_t i = 0; i < unsigned_out_of_range_test_2.size(); ++i) {
{
const auto& f = unsigned_out_of_range_test_2[i]; const auto& f = unsigned_out_of_range_test_2[i];
uint16_t result; uint16_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc::result_out_of_range) { if (answer.ec != std::errc::result_out_of_range) {
std::cerr << "expected error for should be 'result_out_of_range': " << std::quoted(f) << std::endl; std::cerr << "expected error for should be 'result_out_of_range': " << quoted(f) << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// unsigned out of range error test #3 (32 bit) // unsigned out of range error test #3 (32 bit)
const std::vector<std::string> unsigned_out_of_range_test_3{ "2000000000000000000000", "4294967296" }; const std::vector<std::string_view> unsigned_out_of_range_test_3{ "2000000000000000000000", "4294967296" };
for (std::size_t i = 0; i < unsigned_out_of_range_test_3.size(); ++i) for (std::size_t i = 0; i < unsigned_out_of_range_test_3.size(); ++i) {
{
const auto& f = unsigned_out_of_range_test_3[i]; const auto& f = unsigned_out_of_range_test_3[i];
uint32_t result; uint32_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc::result_out_of_range) { if (answer.ec != std::errc::result_out_of_range) {
std::cerr << "expected error for should be 'result_out_of_range': " << std::quoted(f) << std::endl; std::cerr << "expected error for should be 'result_out_of_range': " << quoted(f) << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// unsigned out of range error test #4 (64 bit) // unsigned out of range error test #4 (64 bit)
const std::vector<std::string> unsigned_out_of_range_test_4{ "2000000000000000000000", "18446744073709551616" }; const std::vector<std::string_view> unsigned_out_of_range_test_4{ "2000000000000000000000", "18446744073709551616" };
for (std::size_t i = 0; i < unsigned_out_of_range_test_4.size(); ++i) for (std::size_t i = 0; i < unsigned_out_of_range_test_4.size(); ++i) {
{
const auto& f = unsigned_out_of_range_test_4[i]; const auto& f = unsigned_out_of_range_test_4[i];
uint64_t result; uint64_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc::result_out_of_range) { if (answer.ec != std::errc::result_out_of_range) {
std::cerr << "expected error for should be 'result_out_of_range': " << std::quoted(f) << std::endl; std::cerr << "expected error for should be 'result_out_of_range': " << quoted(f) << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// int pointer test #1 (only numbers) // int pointer test #1 (only numbers)
const std::vector<std::string> int_pointer_test_1 { "0", "010", "-40" }; const std::vector<std::string_view> int_pointer_test_1 { "0", "010", "-40" };
for (std::size_t i = 0; i < int_pointer_test_1.size(); ++i) for (std::size_t i = 0; i < int_pointer_test_1.size(); ++i) {
{
const auto& f = int_pointer_test_1[i]; const auto& f = int_pointer_test_1[i];
int result; int result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc()) { if (answer.ec != std::errc()) {
std::cerr << "could not convert to int for input: " << std::quoted(f) << std::endl; std::cerr << "could not convert to int for input: " << quoted(f) << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
else if (strcmp(answer.ptr, "") != 0) { else if (strcmp(answer.ptr, "") != 0) {
std::cerr << "ptr of result " << std::quoted(f) << " did not match with expected ptr: " << std::quoted("") << std::endl; std::cerr << "ptr of result " << quoted(f) << " did not match with expected ptr: " << quoted("") << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// int pointer test #2 (string behind numbers) // int pointer test #2 (string behind numbers)
const std::string int_pointer_test_2 = "1001 with text"; const std::string_view int_pointer_test_2 = "1001 with text";
const auto& f2 = int_pointer_test_2; const auto& f2 = int_pointer_test_2;
int result2; int result2;
auto answer2 = fast_float::from_chars(f2.data(), f2.data() + f2.size(), result2); auto answer2 = fast_float::from_chars(f2.data(), f2.data() + f2.size(), result2);
if (strcmp(answer2.ptr, " with text") != 0) { if (strcmp(answer2.ptr, " with text") != 0) {
std::cerr << "ptr of result " << std::quoted(f2) << " did not match with expected ptr: " << std::quoted(" with text") << std::endl; std::cerr << "ptr of result " << quoted(f2) << " did not match with expected ptr: " << quoted(" with text") << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
// int pointer test #3 (string with newline behind numbers) // int pointer test #3 (string with newline behind numbers)
const std::string int_pointer_test_3 = "1001 with text\n"; const std::string_view int_pointer_test_3 = "1001 with text\n";
const auto& f3 = int_pointer_test_3; const auto& f3 = int_pointer_test_3;
int result3; int result3;
auto answer3 = fast_float::from_chars(f3.data(), f3.data() + f3.size(), result3); auto answer3 = fast_float::from_chars(f3.data(), f3.data() + f3.size(), result3);
if (strcmp(answer3.ptr, " with text\n") != 0) { if (strcmp(answer3.ptr, " with text\n") != 0) {
std::cerr << "ptr of result " << std::quoted(f3) << " did not match with expected ptr: " << std::quoted(" with text") << std::endl; std::cerr << "ptr of result " << quoted(f3) << " did not match with expected ptr: " << quoted(" with text") << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
// int pointer test #4 (float) // int pointer test #4 (float)
const std::string int_pointer_test_4 = "9.999"; const std::string_view int_pointer_test_4 = "9.999";
const auto& f4 = int_pointer_test_4; const auto& f4 = int_pointer_test_4;
int result4; int result4;
auto answer4 = fast_float::from_chars(f4.data(), f4.data() + f4.size(), result4); auto answer4 = fast_float::from_chars(f4.data(), f4.data() + f4.size(), result4);
if (strcmp(answer4.ptr, ".999") != 0) { if (strcmp(answer4.ptr, ".999") != 0) {
std::cerr << "ptr of result " << std::quoted(f4) << " did not match with expected ptr: " << std::quoted(".999") << std::endl; std::cerr << "ptr of result " << quoted(f4) << " did not match with expected ptr: " << quoted(".999") << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
// int pointer test #5 (invalid int) // int pointer test #5 (invalid int)
const std::string int_pointer_test_5 = "+50"; const std::string_view int_pointer_test_5 = "+50";
const auto& f5 = int_pointer_test_5; const auto& f5 = int_pointer_test_5;
int result5; int result5;
auto answer5 = fast_float::from_chars(f5.data(), f5.data() + f5.size(), result5); auto answer5 = fast_float::from_chars(f5.data(), f5.data() + f5.size(), result5);
if (strcmp(answer5.ptr, "+50") != 0) { if (strcmp(answer5.ptr, "+50") != 0) {
std::cerr << "ptr of result " << std::quoted(f5) << " did not match with expected ptr: " << std::quoted("+50") << std::endl; std::cerr << "ptr of result " << quoted(f5) << " did not match with expected ptr: " << quoted("+50") << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
// unsigned pointer test #2 (string behind numbers) // unsigned pointer test #2 (string behind numbers)
const std::string unsigned_pointer_test_1 = "1001 with text"; const std::string_view unsigned_pointer_test_1 = "1001 with text";
const auto& f6 = unsigned_pointer_test_1; const auto& f6 = unsigned_pointer_test_1;
unsigned result6; unsigned result6;
auto answer6 = fast_float::from_chars(f6.data(), f6.data() + f6.size(), result6); auto answer6 = fast_float::from_chars(f6.data(), f6.data() + f6.size(), result6);
if (strcmp(answer6.ptr, " with text") != 0) { if (strcmp(answer6.ptr, " with text") != 0) {
std::cerr << "ptr of result " << std::quoted(f6) << " did not match with expected ptr: " << std::quoted(" with text") << std::endl; std::cerr << "ptr of result " << quoted(f6) << " did not match with expected ptr: " << quoted(" with text") << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
// unsigned pointer test #2 (invalid unsigned) // unsigned pointer test #2 (invalid unsigned)
const std::string unsigned_pointer_test_2 = "-50"; const std::string_view unsigned_pointer_test_2 = "-50";
const auto& f7 = unsigned_pointer_test_2; const auto& f7 = unsigned_pointer_test_2;
unsigned result7; unsigned result7;
auto answer7 = fast_float::from_chars(f7.data(), f7.data() + f7.size(), result7); auto answer7 = fast_float::from_chars(f7.data(), f7.data() + f7.size(), result7);
if (strcmp(answer7.ptr, "-50") != 0) { if (strcmp(answer7.ptr, "-50") != 0) {
std::cerr << "ptr of result " << std::quoted(f7) << " did not match with expected ptr: " << std::quoted("-50") << std::endl; std::cerr << "ptr of result " << quoted(f7) << " did not match with expected ptr: " << quoted("-50") << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
// int base 2 test // int base 2 test
const std::vector<int> int_base_2_test_expected { 0, 1, 4, 2, -1 }; const std::vector<int> int_base_2_test_expected { 0, 1, 4, 2, -1 };
const std::vector<std::string> int_base_2_test { "0", "1", "100", "010", "-1" }; const std::vector<std::string_view> int_base_2_test { "0", "1", "100", "010", "-1" };
for (std::size_t i = 0; i < int_base_2_test.size(); ++i) for (std::size_t i = 0; i < int_base_2_test.size(); ++i) {
{
const auto& f = int_base_2_test[i]; const auto& f = int_base_2_test[i];
int result; int result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 2); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 2);
if (answer.ec != std::errc()) { if (answer.ec != std::errc()) {
std::cerr << "could not convert to int for input: " << std::quoted(f) << std::endl; std::cerr << "could not convert to int for input: " << quoted(f) << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
else if (result != int_base_2_test_expected[i]) { else if (result != int_base_2_test_expected[i]) {
std::cerr << "result " << std::quoted(f) << " did not match with expected int: " << int_base_2_test_expected[i] << std::endl; std::cerr << "result " << quoted(f) << " did not match with expected int: " << int_base_2_test_expected[i] << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// unsigned base 2 test // unsigned base 2 test
const std::vector<unsigned> unsigned_base_2_test_expected { 0, 1, 4, 2 }; const std::vector<unsigned> unsigned_base_2_test_expected { 0, 1, 4, 2 };
const std::vector<std::string> unsigned_base_2_test { "0", "1", "100", "010" }; const std::vector<std::string_view> unsigned_base_2_test { "0", "1", "100", "010" };
for (std::size_t i = 0; i < unsigned_base_2_test.size(); ++i) for (std::size_t i = 0; i < unsigned_base_2_test.size(); ++i) {
{
const auto& f = unsigned_base_2_test[i]; const auto& f = unsigned_base_2_test[i];
unsigned result; unsigned result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 2); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 2);
if (answer.ec != std::errc()) { if (answer.ec != std::errc()) {
std::cerr << "could not convert to unsigned for input: " << std::quoted(f) << std::endl; std::cerr << "could not convert to unsigned for input: " << quoted(f) << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
else if (result != unsigned_base_2_test_expected[i]) { else if (result != unsigned_base_2_test_expected[i]) {
std::cerr << "result " << std::quoted(f) << " did not match with expected unsigned: " << unsigned_base_2_test_expected[i] << std::endl; std::cerr << "result " << quoted(f) << " did not match with expected unsigned: " << unsigned_base_2_test_expected[i] << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// int invalid error base 2 test // int invalid error base 2 test
const std::vector<std::string> int_invalid_argument_base_2_test{ "2", "A", "-2" }; const std::vector<std::string_view> int_invalid_argument_base_2_test{ "2", "A", "-2" };
for (std::size_t i = 0; i < int_invalid_argument_base_2_test.size(); ++i) for (std::size_t i = 0; i < int_invalid_argument_base_2_test.size(); ++i) {
{
const auto& f = int_invalid_argument_base_2_test[i]; const auto& f = int_invalid_argument_base_2_test[i];
int result; int result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 2); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 2);
if (answer.ec != std::errc::invalid_argument) { if (answer.ec != std::errc::invalid_argument) {
std::cerr << "expected error should be 'invalid_argument' for: " << std::quoted(f) << std::endl; std::cerr << "expected error should be 'invalid_argument' for: " << quoted(f) << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// unsigned invalid error base 2 test // unsigned invalid error base 2 test
const std::vector<std::string> unsigned_invalid_argument_base_2_test{ "2", "A", "-1", "-2" }; const std::vector<std::string_view> unsigned_invalid_argument_base_2_test{ "2", "A", "-1", "-2" };
for (std::size_t i = 0; i < unsigned_invalid_argument_base_2_test.size(); ++i) for (std::size_t i = 0; i < unsigned_invalid_argument_base_2_test.size(); ++i) {
{
const auto& f = unsigned_invalid_argument_base_2_test[i]; const auto& f = unsigned_invalid_argument_base_2_test[i];
unsigned result; unsigned result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 2); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 2);
if (answer.ec != std::errc::invalid_argument) { if (answer.ec != std::errc::invalid_argument) {
std::cerr << "expected error should be 'invalid_argument' for: " << std::quoted(f) << std::endl; std::cerr << "expected error should be 'invalid_argument' for: " << quoted(f) << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// octal test // octal test
const std::vector<int> base_octal_test_expected {0, 1, 7, 8, 9}; const std::vector<int> base_octal_test_expected {0, 1, 7, 8, 9};
const std::vector<std::string> base_octal_test { "0", "1", "07", "010", "0011" }; const std::vector<std::string_view> base_octal_test { "0", "1", "07", "010", "0011" };
for (std::size_t i = 0; i < base_octal_test.size(); ++i) for (std::size_t i = 0; i < base_octal_test.size(); ++i) {
{
const auto& f = base_octal_test[i]; const auto& f = base_octal_test[i];
int result; int result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 8); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 8);
if (answer.ec != std::errc()) { if (answer.ec != std::errc()) {
std::cerr << "could not convert to int for input: " << std::quoted(f) << std::endl; std::cerr << "could not convert to int for input: " << quoted(f) << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
else if (result != base_octal_test_expected[i]) { else if (result != base_octal_test_expected[i]) {
std::cerr << "result " << std::quoted(f) << " did not match with expected int: " << base_octal_test_expected[i] << std::endl; std::cerr << "result " << quoted(f) << " did not match with expected int: " << base_octal_test_expected[i] << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// hex test // hex test
const std::vector<int> base_hex_test_expected { 0, 1, 15, 31, 0, 16}; const std::vector<int> base_hex_test_expected { 0, 1, 15, 31, 0, 16};
const std::vector<std::string> base_hex_test { "0", "1", "F", "01f", "0x11", "10X11" }; const std::vector<std::string_view> base_hex_test { "0", "1", "F", "01f", "0x11", "10X11" };
for (std::size_t i = 0; i < base_hex_test.size(); ++i) for (std::size_t i = 0; i < base_hex_test.size(); ++i) {
{
const auto& f = base_hex_test[i]; const auto& f = base_hex_test[i];
int result; int result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 16); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 16);
if (answer.ec != std::errc()) { if (answer.ec != std::errc()) {
std::cerr << "could not convert to int for input: " << std::quoted(f) << std::endl; std::cerr << "could not convert to int for input: " << quoted(f) << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
else if (result != base_hex_test_expected[i]) { else if (result != base_hex_test_expected[i]) {
std::cerr << "result " << std::quoted(f) << " did not match with expected int: " << base_hex_test_expected[i] << std::endl; std::cerr << "result " << quoted(f) << " did not match with expected int: " << base_hex_test_expected[i] << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// invalid base test #1 (-1) // invalid base test #1 (-1)
const std::vector<std::string> invalid_base_test_1 { "0", "1", "-1", "F", "10Z" }; const std::vector<std::string_view> invalid_base_test_1 { "0", "1", "-1", "F", "10Z" };
for (std::size_t i = 0; i < invalid_base_test_1.size(); ++i) for (std::size_t i = 0; i < invalid_base_test_1.size(); ++i) {
{
const auto& f = invalid_base_test_1[i]; const auto& f = invalid_base_test_1[i];
int result; int result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, -1); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, -1);
if (answer.ec != std::errc::invalid_argument) { if (answer.ec != std::errc::invalid_argument) {
std::cerr << "expected error should be 'invalid_argument' for: " << std::quoted(f) << std::endl; std::cerr << "expected error should be 'invalid_argument' for: " << quoted(f) << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// invalid base test #2 (37) // invalid base test #2 (37)
const std::vector<std::string> invalid_base_test_2 { "0", "1", "F", "Z", "10Z" }; const std::vector<std::string_view> invalid_base_test_2 { "0", "1", "F", "Z", "10Z" };
for (std::size_t i = 0; i < invalid_base_test_2.size(); ++i) for (std::size_t i = 0; i < invalid_base_test_2.size(); ++i) {
{
const auto& f = invalid_base_test_2[i]; const auto& f = invalid_base_test_2[i];
int result; int result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 37); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 37);
if (answer.ec != std::errc::invalid_argument) { if (answer.ec != std::errc::invalid_argument) {
std::cerr << "expected error should be 'invalid_argument' for: " << std::quoted(f) << std::endl; std::cerr << "expected error should be 'invalid_argument' for: " << quoted(f) << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// int out of range error base test (64 bit) // int out of range error base test (64 bit)
const std::vector<std::string> int_out_of_range_base_test { "1000000000000000000000000000000000000000000000000000000000000000", const std::vector<std::string_view> int_out_of_range_base_test { "1000000000000000000000000000000000000000000000000000000000000000",
"-1000000000000000000000000000000000000000000000000000000000000001", "-1000000000000000000000000000000000000000000000000000000000000001",
"2021110011022210012102010021220101220222", "2021110011022210012102010021220101220222",
"-2021110011022210012102010021220101221000", "-2021110011022210012102010021220101221000",
@ -504,19 +503,18 @@ int main()
"1Y2P0IJ32E8E8", "1Y2P0IJ32E8E8",
"-1Y2P0IJ32E8E9" }; "-1Y2P0IJ32E8E9" };
for (std::size_t i = 0; i < int_out_of_range_base_test.size(); ++i) for (std::size_t i = 0; i < int_out_of_range_base_test.size(); ++i) {
{
const auto& f = int_out_of_range_base_test[i]; const auto& f = int_out_of_range_base_test[i];
int64_t result; int64_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, int(2 + (i / 2))); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, int(2 + (i / 2)));
if (answer.ec != std::errc::result_out_of_range) { if (answer.ec != std::errc::result_out_of_range) {
std::cerr << "expected error for should be 'result_out_of_range': " << std::quoted(f) << std::endl; std::cerr << "expected error for should be 'result_out_of_range': " << quoted(f) << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// unsigned out of range error base test (64 bit) // unsigned out of range error base test (64 bit)
const std::vector<std::string> unsigned_out_of_range_base_test { "10000000000000000000000000000000000000000000000000000000000000000", const std::vector<std::string_view> unsigned_out_of_range_base_test { "10000000000000000000000000000000000000000000000000000000000000000",
"11112220022122120101211020120210210211221", "11112220022122120101211020120210210211221",
"100000000000000000000000000000000", "100000000000000000000000000000000",
"2214220303114400424121122431", "2214220303114400424121122431",
@ -552,20 +550,19 @@ int main()
"5G24A25TWKWFG", "5G24A25TWKWFG",
"3W5E11264SGSG" }; "3W5E11264SGSG" };
int base_unsigned = 2; int base_unsigned = 2;
for (std::size_t i = 0; i < unsigned_out_of_range_base_test.size(); ++i) for (std::size_t i = 0; i < unsigned_out_of_range_base_test.size(); ++i) {
{
const auto& f = unsigned_out_of_range_base_test[i]; const auto& f = unsigned_out_of_range_base_test[i];
uint64_t result; uint64_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, base_unsigned); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, base_unsigned);
if (answer.ec != std::errc::result_out_of_range) { if (answer.ec != std::errc::result_out_of_range) {
std::cerr << "expected error for should be 'result_out_of_range': " << std::quoted(f) << std::endl; std::cerr << "expected error for should be 'result_out_of_range': " << quoted(f) << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
++base_unsigned; ++base_unsigned;
} }
// just within range base test (64 bit) // just within range base test (64 bit)
const std::vector<std::string> int_within_range_base_test { "111111111111111111111111111111111111111111111111111111111111111", const std::vector<std::string_view> int_within_range_base_test { "111111111111111111111111111111111111111111111111111111111111111",
"-1000000000000000000000000000000000000000000000000000000000000000", "-1000000000000000000000000000000000000000000000000000000000000000",
"2021110011022210012102010021220101220221", "2021110011022210012102010021220101220221",
"-2021110011022210012102010021220101220222", "-2021110011022210012102010021220101220222",
@ -636,19 +633,18 @@ int main()
"1Y2P0IJ32E8E7", "1Y2P0IJ32E8E7",
"-1Y2P0IJ32E8E8" }; "-1Y2P0IJ32E8E8" };
for (std::size_t i = 0; i < int_within_range_base_test.size(); ++i) for (std::size_t i = 0; i < int_within_range_base_test.size(); ++i) {
{
const auto& f = int_within_range_base_test[i]; const auto& f = int_within_range_base_test[i];
int64_t result; int64_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, int(2 + (i / 2))); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, int(2 + (i / 2)));
if (answer.ec != std::errc()) { if (answer.ec != std::errc()) {
std::cerr << "converting " << std::quoted(f) << " to int failed (most likely out of range)" << std::endl; std::cerr << "converting " << quoted(f) << " to int failed (most likely out of range)" << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// unsigned within range base test (64 bit) // unsigned within range base test (64 bit)
const std::vector<std::string> unsigned_within_range_base_test { "1111111111111111111111111111111111111111111111111111111111111111", const std::vector<std::string_view> unsigned_within_range_base_test { "1111111111111111111111111111111111111111111111111111111111111111",
"11112220022122120101211020120210210211220", "11112220022122120101211020120210210211220",
"33333333333333333333333333333333", "33333333333333333333333333333333",
"2214220303114400424121122430", "2214220303114400424121122430",
@ -684,20 +680,19 @@ int main()
"5G24A25TWKWFF", "5G24A25TWKWFF",
"3W5E11264SGSF" }; "3W5E11264SGSF" };
int base_unsigned2 = 2; int base_unsigned2 = 2;
for (std::size_t i = 0; i < unsigned_within_range_base_test.size(); ++i) for (std::size_t i = 0; i < unsigned_within_range_base_test.size(); ++i) {
{
const auto& f = unsigned_within_range_base_test[i]; const auto& f = unsigned_within_range_base_test[i];
uint64_t result; uint64_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, base_unsigned2); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, base_unsigned2);
if (answer.ec != std::errc()) { if (answer.ec != std::errc()) {
std::cerr << "converting " << std::quoted(f) << " to unsigned failed (most likely out of range)" << std::endl; std::cerr << "converting " << quoted(f) << " to unsigned failed (most likely out of range)" << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
++base_unsigned2; ++base_unsigned2;
} }
// int leading zeros test // int leading zeros test
const std::vector<std::string> int_leading_zeros_test { "00000000000000000000000000000000000000000000000000000000000000000000001111110111", const std::vector<std::string_view> int_leading_zeros_test { "00000000000000000000000000000000000000000000000000000000000000000000001111110111",
"000000000000000000000000000000000000000000000000001101121", "000000000000000000000000000000000000000000000000001101121",
"000000000000000000000000000000000000000033313", "000000000000000000000000000000000000000033313",
"00000000000000000000000000000013030", "00000000000000000000000000000013030",
@ -733,20 +728,43 @@ int main()
"00000000000000000000T0", "00000000000000000000T0",
"00000000000000000000S7" }; "00000000000000000000S7" };
for (std::size_t i = 0; i < int_leading_zeros_test.size(); ++i) for (std::size_t i = 0; i < int_leading_zeros_test.size(); ++i) {
{
const auto& f = int_leading_zeros_test[i]; const auto& f = int_leading_zeros_test[i];
int result; int result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, int(i + 2)); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, int(i + 2));
if (answer.ec != std::errc()) { if (answer.ec != std::errc()) {
std::cerr << "could not convert to int for input: " << std::quoted(f) << std::endl; std::cerr << "could not convert to int for input: " << quoted(f) << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
else if (result != 1015) { else if (result != 1015) {
std::cerr << "result " << std::quoted(f) << " did not match with expected int: " << 1015 << std::endl; std::cerr << "result " << quoted(f) << " did not match with expected int: " << 1015 << std::endl;
return EXIT_FAILURE;
}
}
// issue 235
{
std::vector<char> s = {'0'};
s.shrink_to_fit();
int foo;
auto answer = fast_float::from_chars(s.data(), s.data() + s.size(), foo);
if (answer.ec != std::errc()) {
std::cerr << "could not convert to int for input: '0'" << std::endl;
return EXIT_FAILURE;
}
else if (foo != 0) {
std::cerr << "expected zero: " << foo << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
#else
#include <iostream>
#include <cstdlib>
int main() {
std::cerr << "The test requires C++17." << std::endl;
return EXIT_SUCCESS;
}
#endif