| #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 <iostream> | |
| #include <vector> | |
| #include <string_view> | |
| #include <string> | |
| #include <cstring> | |
| #include <random> | |
| #include <algorithm> | |
| #include "fast_float/fast_float.h" | |
| #include <cstdint> | |
| /* | |
| all tests conducted are to check fast_float::from_chars functionality with int | |
| and unsigned test cases include: int basic tests - numbers only, numbers with | |
| strings behind, decimals, negative numbers unsigned basic tests - numbers only, | |
| numbers with strings behind, decimals int invalid tests - strings only, strings | |
| with numbers behind, space in front of number, plus sign in front of number | |
| unsigned invalid tests - strings only, strings with numbers behind, space in | |
| front of number, plus/minus sign in front of number int out of range tests - | |
| numbers exceeding int bit size for 8, 16, 32, and 64 bits unsigned out of range | |
| tests - numbers exceeding unsigned bit size 8, 16, 32, and 64 bits int pointer | |
| tests - points to first character that is not recognized as int unsigned pointer | |
| tests - points to first character that is not recognized as unsigned | |
| int/unsigned base 2 tests - numbers are converted from binary to decimal | |
| octal tests - numbers are converted from octal to decimal | |
| hex tests - numbers are converted from hex to decimal (Note: 0x and 0X are | |
| considered invalid) invalid base tests - any base not within 2-36 is invalid out | |
| of range base tests - numbers exceeding int/unsigned bit size after converted | |
| from base (Note: only 64 bit int and unsigned are tested) within range base | |
| tests - max/min numbers are still within int/unsigned bit size after converted | |
| from base (Note: only 64 bit int and unsigned are tested) leading zeros tests - | |
| ignores all zeroes in front of valid number after converted from base | |
| */ | |
| int main() { | |
| // int basic test | |
| std::vector<int> const int_basic_test_expected{0, 10, -40, 1001, 9}; | |
| std::vector<std::string_view> const int_basic_test{"0", "10 ", "-40", | |
| "1001 with text", "9.999"}; | |
| for (std::size_t i = 0; i < int_basic_test.size(); ++i) { | |
| auto const f = int_basic_test[i]; | |
| int result; | |
| auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); | |
| if (answer.ec != std::errc()) { | |
| if (answer.ec == std::errc::invalid_argument) { | |
| std::cerr << "could not convert to int for input: \"" << f | |
| << "\" because of invalid argument" << std::endl; | |
| } else if (answer.ec == std::errc::result_out_of_range) { | |
| std::cerr << "could not convert to int for input: \"" << f | |
| << "\" because it's out of range" << std::endl; | |
| } else { | |
| std::cerr << "could not convert to int for input: \"" << f | |
| << "\" because of an unknown error" << std::endl; | |
| } | |
| return EXIT_FAILURE; | |
| } else if (result != int_basic_test_expected[i]) { | |
| std::cerr << "result \"" << f << "\" did not match with expected int: " | |
| << int_basic_test_expected[i] << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // unsigned basic test | |
| std::vector<unsigned> const unsigned_basic_test_expected{0, 10, 1001, 9}; | |
| std::vector<std::string_view> const unsigned_basic_test{ | |
| "0", "10 ", "1001 with text", "9.999"}; | |
| for (std::size_t i = 0; i < unsigned_basic_test.size(); ++i) { | |
| auto const &f = unsigned_basic_test[i]; | |
| unsigned result; | |
| auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); | |
| if (answer.ec != std::errc()) { | |
| std::cerr << "could not convert to unsigned for input: \"" << f << "\"" | |
| << std::endl; | |
| return EXIT_FAILURE; | |
| } else if (result != unsigned_basic_test_expected[i]) { | |
| std::cerr << "result \"" << f | |
| << "\" did not match with expected unsigned: " | |
| << unsigned_basic_test_expected[i] << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // char basic test | |
| std::vector<char> const char_basic_test_expected{0, 10, 40, 100, 9}; | |
| std::vector<std::string_view> const char_basic_test{"0", "10 ", "40", | |
| "100 with text", "9.999"}; | |
| for (std::size_t i = 0; i < char_basic_test.size(); ++i) { | |
| auto const f = char_basic_test[i]; | |
| char result; | |
| auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); | |
| if (answer.ec != std::errc()) { | |
| std::cerr << "could not convert to char for input: \"" << f | |
| << "\" because of invalid argument" << std::endl; | |
| return EXIT_FAILURE; | |
| } else if (result != char_basic_test_expected[i]) { | |
| std::cerr << "result \"" << f << "\" did not match with expected char: " | |
| << static_cast<int>(char_basic_test_expected[i]) << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // short basic test | |
| std::vector<short> const short_basic_test_expected{0, 10, -40, 1001, 9}; | |
| std::vector<std::string_view> const short_basic_test{ | |
| "0", "10 ", "-40", "1001 with text", "9.999"}; | |
| for (std::size_t i = 0; i < short_basic_test.size(); ++i) { | |
| auto const f = short_basic_test[i]; | |
| short result; | |
| auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); | |
| if (answer.ec != std::errc()) { | |
| std::cerr << "could not convert to short for input: \"" << f | |
| << "\" because of invalid argument" << std::endl; | |
| return EXIT_FAILURE; | |
| } else if (result != short_basic_test_expected[i]) { | |
| std::cerr << "result \"" << f << "\" did not match with expected short: " | |
| << short_basic_test_expected[i] << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // long basic test | |
| std::vector<long> const long_basic_test_expected{0, 10, -40, 1001, 9}; | |
| std::vector<std::string_view> const long_basic_test{ | |
| "0", "10 ", "-40", "1001 with text", "9.999"}; | |
| for (std::size_t i = 0; i < long_basic_test.size(); ++i) { | |
| auto const f = long_basic_test[i]; | |
| long result; | |
| auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); | |
| if (answer.ec != std::errc()) { | |
| std::cerr << "could not convert to long for input: \"" << f | |
| << "\" because of invalid argument" << std::endl; | |
| return EXIT_FAILURE; | |
| } else if (result != long_basic_test_expected[i]) { | |
| std::cerr << "result \"" << f << "\" did not match with expected long: " | |
| << long_basic_test_expected[i] << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // long long basic test | |
| std::vector<long long> const long_long_basic_test_expected{0, 10, -40, 1001, | |
| 9}; | |
| std::vector<std::string_view> const long_long_basic_test{ | |
| "0", "10 ", "-40", "1001 with text", "9.999"}; | |
| for (std::size_t i = 0; i < long_long_basic_test.size(); ++i) { | |
| auto const f = long_long_basic_test[i]; | |
| long long result; | |
| auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); | |
| if (answer.ec != std::errc()) { | |
| std::cerr << "could not convert to long long for input: \"" << f | |
| << "\" because of invalid argument" << std::endl; | |
| return EXIT_FAILURE; | |
| } else if (result != long_long_basic_test_expected[i]) { | |
| std::cerr << "result \"" << f | |
| << "\" did not match with expected long long: " | |
| << long_long_basic_test_expected[i] << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // unsigned char basic test | |
| std::vector<unsigned char> const unsigned_char_basic_test_expected{0, 10, 100, | |
| 9}; | |
| std::vector<std::string_view> const unsigned_char_basic_test{ | |
| "0", "10 ", "100 with text", "9.999"}; | |
| for (std::size_t i = 0; i < unsigned_char_basic_test.size(); ++i) { | |
| auto const &f = unsigned_char_basic_test[i]; | |
| unsigned char result; | |
| auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); | |
| if (answer.ec != std::errc()) { | |
| std::cerr << "could not convert to unsigned char for input: \"" << f | |
| << "\"" << std::endl; | |
| return EXIT_FAILURE; | |
| } else if (result != unsigned_char_basic_test_expected[i]) { | |
| std::cerr << "result \"" << f | |
| << "\" did not match with expected unsigned char: " | |
| << static_cast<int>(unsigned_char_basic_test_expected[i]) | |
| << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // unsigned short basic test | |
| std::vector<unsigned short> const unsigned_short_basic_test_expected{0, 10, | |
| 1001, 9}; | |
| std::vector<std::string_view> const unsigned_short_basic_test{ | |
| "0", "10 ", "1001 with text", "9.999"}; | |
| for (std::size_t i = 0; i < unsigned_short_basic_test.size(); ++i) { | |
| auto const &f = unsigned_short_basic_test[i]; | |
| unsigned short result; | |
| auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); | |
| if (answer.ec != std::errc()) { | |
| std::cerr << "could not convert to unsigned short for input: \"" << f | |
| << "\"" << std::endl; | |
| return EXIT_FAILURE; | |
| } else if (result != unsigned_short_basic_test_expected[i]) { | |
| std::cerr << "result \"" << f | |
| << "\" did not match with expected unsigned short: " | |
| << unsigned_short_basic_test_expected[i] << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // unsigned long basic test | |
| std::vector<unsigned long> const unsigned_long_basic_test_expected{0, 10, | |
| 1001, 9}; | |
| std::vector<std::string_view> const unsigned_long_basic_test{ | |
| "0", "10 ", "1001 with text", "9.999"}; | |
| for (std::size_t i = 0; i < unsigned_long_basic_test.size(); ++i) { | |
| auto const &f = unsigned_long_basic_test[i]; | |
| unsigned long result; | |
| auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); | |
| if (answer.ec != std::errc()) { | |
| std::cerr << "could not convert to unsigned long for input: \"" << f | |
| << "\"" << std::endl; | |
| return EXIT_FAILURE; | |
| } else if (result != unsigned_long_basic_test_expected[i]) { | |
| std::cerr << "result \"" << f | |
| << "\" did not match with expected unsigned long: " | |
| << unsigned_long_basic_test_expected[i] << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // unsigned long long basic test | |
| std::vector<unsigned long long> const unsigned_long_long_basic_test_expected{ | |
| 0, 10, 1001, 9}; | |
| std::vector<std::string_view> const unsigned_long_long_basic_test{ | |
| "0", "10 ", "1001 with text", "9.999"}; | |
| for (std::size_t i = 0; i < unsigned_long_long_basic_test.size(); ++i) { | |
| auto const &f = unsigned_long_long_basic_test[i]; | |
| unsigned long long result; | |
| auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); | |
| if (answer.ec != std::errc()) { | |
| std::cerr << "could not convert to unsigned long long for input: \"" << f | |
| << "\"" << std::endl; | |
| return EXIT_FAILURE; | |
| } else if (result != unsigned_long_long_basic_test_expected[i]) { | |
| std::cerr << "result \"" << f | |
| << "\" did not match with expected unsigned long long: " | |
| << unsigned_long_long_basic_test_expected[i] << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // bool basic test | |
| std::vector<bool> const bool_basic_test_expected{false, true}; | |
| std::vector<std::string_view> const bool_basic_test{"0", "1"}; | |
| for (std::size_t i = 0; i < bool_basic_test.size(); ++i) { | |
| auto const &f = bool_basic_test[i]; | |
| bool result; | |
| auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); | |
| if (answer.ec != std::errc()) { | |
| std::cerr << "could not convert to bool for input: \"" << f << "\"" | |
| << std::endl; | |
| return EXIT_FAILURE; | |
| } else if (result != bool_basic_test_expected[i]) { | |
| std::cerr << "result \"" << f << "\" did not match with expected bool: " | |
| << (bool_basic_test_expected[i] ? "true" : "false") | |
| << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // int invalid error test | |
| std::vector<std::string_view> const int_invalid_argument_test{ | |
| "text", "text with 1002", "+50", " 50"}; | |
| for (std::size_t i = 0; i < int_invalid_argument_test.size(); ++i) { | |
| auto const &f = int_invalid_argument_test[i]; | |
| int result; | |
| auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); | |
| if (answer.ec != std::errc::invalid_argument) { | |
| std::cerr << "expected error should be 'invalid_argument' for: \"" << f | |
| << "\"" << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // unsigned invalid error test | |
| std::vector<std::string_view> const unsigned_invalid_argument_test{ | |
| "text", "text with 1002", "+50", " 50", "-50"}; | |
| for (std::size_t i = 0; i < unsigned_invalid_argument_test.size(); ++i) { | |
| auto const &f = unsigned_invalid_argument_test[i]; | |
| unsigned result; | |
| auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); | |
| if (answer.ec != std::errc::invalid_argument) { | |
| std::cerr << "expected error should be 'invalid_argument' for: \"" << f | |
| << "\"" << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // int out of range error test #1 (8 bit) | |
| std::vector<std::string_view> const int_out_of_range_test_1{ | |
| "2000000000000000000000", "128", "-129"}; | |
| for (std::size_t i = 0; i < int_out_of_range_test_1.size(); ++i) { | |
| auto const &f = int_out_of_range_test_1[i]; | |
| int8_t result; | |
| auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); | |
| if (answer.ec != std::errc::result_out_of_range) { | |
| std::cerr << "expected error for should be 'result_out_of_range': \"" << f | |
| << "\"" << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // int out of range error test #2 (16 bit) | |
| std::vector<std::string_view> const int_out_of_range_test_2{ | |
| "2000000000000000000000", "32768", "-32769"}; | |
| for (std::size_t i = 0; i < int_out_of_range_test_2.size(); ++i) { | |
| auto const &f = int_out_of_range_test_2[i]; | |
| int16_t result; | |
| auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); | |
| if (answer.ec != std::errc::result_out_of_range) { | |
| std::cerr << "expected error for should be 'result_out_of_range': \"" << f | |
| << "\"" << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // int out of range error test #3 (32 bit) | |
| std::vector<std::string_view> const int_out_of_range_test_3{ | |
| "2000000000000000000000", "2147483648", "-2147483649"}; | |
| for (std::size_t i = 0; i < int_out_of_range_test_3.size(); ++i) { | |
| auto const &f = int_out_of_range_test_3[i]; | |
| int32_t result; | |
| auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); | |
| if (answer.ec != std::errc::result_out_of_range) { | |
| std::cerr << "expected error for should be 'result_out_of_range': \"" << f | |
| << "\"" << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // int out of range error test #4 (64 bit) | |
| std::vector<std::string_view> const int_out_of_range_test_4{ | |
| "2000000000000000000000", "9223372036854775808", "-9223372036854775809"}; | |
| for (std::size_t i = 0; i < int_out_of_range_test_4.size(); ++i) { | |
| auto const &f = int_out_of_range_test_4[i]; | |
| int64_t result; | |
| auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); | |
| if (answer.ec != std::errc::result_out_of_range) { | |
| std::cerr << "expected error for should be 'result_out_of_range': \"" << f | |
| << "\"" << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // unsigned out of range error test #1 (8 bit) | |
| std::vector<std::string_view> const unsigned_out_of_range_test_1{ | |
| "2000000000000000000000", "256"}; | |
| for (std::size_t i = 0; i < unsigned_out_of_range_test_1.size(); ++i) { | |
| auto const &f = unsigned_out_of_range_test_1[i]; | |
| uint8_t result; | |
| auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); | |
| if (answer.ec != std::errc::result_out_of_range) { | |
| std::cerr << "expected error for should be 'result_out_of_range': \"" << f | |
| << "\"" << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // unsigned out of range error test #2 (16 bit) | |
| std::vector<std::string_view> const unsigned_out_of_range_test_2{ | |
| "2000000000000000000000", "65536"}; | |
| for (std::size_t i = 0; i < unsigned_out_of_range_test_2.size(); ++i) { | |
| auto const &f = unsigned_out_of_range_test_2[i]; | |
| uint16_t result; | |
| auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); | |
| if (answer.ec != std::errc::result_out_of_range) { | |
| std::cerr << "expected error for should be 'result_out_of_range': \"" << f | |
| << "\"" << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // unsigned out of range error test #3 (32 bit) | |
| std::vector<std::string_view> const unsigned_out_of_range_test_3{ | |
| "2000000000000000000000", "4294967296"}; | |
| for (std::size_t i = 0; i < unsigned_out_of_range_test_3.size(); ++i) { | |
| auto const &f = unsigned_out_of_range_test_3[i]; | |
| uint32_t result; | |
| auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); | |
| if (answer.ec != std::errc::result_out_of_range) { | |
| std::cerr << "expected error for should be 'result_out_of_range': \"" << f | |
| << "\"" << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // unsigned out of range error test #4 (64 bit) | |
| std::vector<std::string_view> const unsigned_out_of_range_test_4{ | |
| "2000000000000000000000", "18446744073709551616"}; | |
| for (std::size_t i = 0; i < unsigned_out_of_range_test_4.size(); ++i) { | |
| auto const &f = unsigned_out_of_range_test_4[i]; | |
| uint64_t result; | |
| auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); | |
| if (answer.ec != std::errc::result_out_of_range) { | |
| std::cerr << "expected error for should be 'result_out_of_range': \"" << f | |
| << "\"" << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // int pointer test #1 (only numbers) | |
| std::vector<std::string_view> const int_pointer_test_1{"0", "010", "-40"}; | |
| for (std::size_t i = 0; i < int_pointer_test_1.size(); ++i) { | |
| auto const &f = int_pointer_test_1[i]; | |
| int result; | |
| auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); | |
| if (answer.ec != std::errc()) { | |
| std::cerr << "could not convert to int for input: \"" << f << "\"" | |
| << std::endl; | |
| return EXIT_FAILURE; | |
| } else if (strcmp(answer.ptr, "") != 0) { | |
| std::cerr << "ptr of result " << f | |
| << " did not match with expected ptr: \"\"" << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // int pointer test #2 (string behind numbers) | |
| std::string_view const int_pointer_test_2 = "1001 with text"; | |
| auto const &f2 = int_pointer_test_2; | |
| int result2; | |
| auto answer2 = | |
| fast_float::from_chars(f2.data(), f2.data() + f2.size(), result2); | |
| if (strcmp(answer2.ptr, " with text") != 0) { | |
| std::cerr << "ptr of result " << f2 | |
| << " did not match with expected ptr: \"with text\"" << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| // int pointer test #3 (string with newline behind numbers) | |
| std::string_view const int_pointer_test_3 = "1001 with text\n"; | |
| auto const &f3 = int_pointer_test_3; | |
| int result3; | |
| auto answer3 = | |
| fast_float::from_chars(f3.data(), f3.data() + f3.size(), result3); | |
| if (strcmp(answer3.ptr, " with text\n") != 0) { | |
| std::cerr << "ptr of result " << f3 | |
| << " did not match with expected ptr: with text" << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| // int pointer test #4 (float) | |
| std::string_view const int_pointer_test_4 = "9.999"; | |
| auto const &f4 = int_pointer_test_4; | |
| int result4; | |
| auto answer4 = | |
| fast_float::from_chars(f4.data(), f4.data() + f4.size(), result4); | |
| if (strcmp(answer4.ptr, ".999") != 0) { | |
| std::cerr << "ptr of result " << f4 | |
| << " did not match with expected ptr: .999" << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| // int pointer test #5 (invalid int) | |
| std::string_view const int_pointer_test_5 = "+50"; | |
| auto const &f5 = int_pointer_test_5; | |
| int result5; | |
| auto answer5 = | |
| fast_float::from_chars(f5.data(), f5.data() + f5.size(), result5); | |
| if (strcmp(answer5.ptr, "+50") != 0) { | |
| std::cerr << "ptr of result " << f5 | |
| << " did not match with expected ptr: +50" << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| // unsigned pointer test #2 (string behind numbers) | |
| std::string_view const unsigned_pointer_test_1 = "1001 with text"; | |
| auto const &f6 = unsigned_pointer_test_1; | |
| unsigned result6; | |
| auto answer6 = | |
| fast_float::from_chars(f6.data(), f6.data() + f6.size(), result6); | |
| if (strcmp(answer6.ptr, " with text") != 0) { | |
| std::cerr << "ptr of result " << f6 | |
| << " did not match with expected ptr: with text" << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| // unsigned pointer test #2 (invalid unsigned) | |
| std::string_view const unsigned_pointer_test_2 = "-50"; | |
| auto const &f7 = unsigned_pointer_test_2; | |
| unsigned result7; | |
| auto answer7 = | |
| fast_float::from_chars(f7.data(), f7.data() + f7.size(), result7); | |
| if (strcmp(answer7.ptr, "-50") != 0) { | |
| std::cerr << "ptr of result " << f7 | |
| << " did not match with expected ptr: -50" << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| // int base 2 test | |
| std::vector<int> const int_base_2_test_expected{0, 1, 4, 2, -1}; | |
| std::vector<std::string_view> const int_base_2_test{"0", "1", "100", "010", | |
| "-1"}; | |
| for (std::size_t i = 0; i < int_base_2_test.size(); ++i) { | |
| auto const f = int_base_2_test[i]; | |
| int result; | |
| auto answer = | |
| fast_float::from_chars(f.data(), f.data() + f.size(), result, 2); | |
| if (answer.ec != std::errc()) { | |
| std::cerr << "could not convert to int for input: \"" << f << "\"" | |
| << std::endl; | |
| return EXIT_FAILURE; | |
| } else if (result != int_base_2_test_expected[i]) { | |
| std::cerr << "result " << f << " did not match with expected int: " | |
| << int_base_2_test_expected[i] << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // unsigned base 2 test | |
| std::vector<unsigned> const unsigned_base_2_test_expected{0, 1, 4, 2}; | |
| std::vector<std::string_view> const unsigned_base_2_test{"0", "1", "100", | |
| "010"}; | |
| for (std::size_t i = 0; i < unsigned_base_2_test.size(); ++i) { | |
| auto const &f = unsigned_base_2_test[i]; | |
| unsigned result; | |
| auto answer = | |
| fast_float::from_chars(f.data(), f.data() + f.size(), result, 2); | |
| if (answer.ec != std::errc()) { | |
| std::cerr << "could not convert to unsigned for input: \"" << f << "\"" | |
| << std::endl; | |
| return EXIT_FAILURE; | |
| } else if (result != unsigned_base_2_test_expected[i]) { | |
| std::cerr << "result " << f << " did not match with expected unsigned: " | |
| << unsigned_base_2_test_expected[i] << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // int invalid error base 2 test | |
| std::vector<std::string_view> const int_invalid_argument_base_2_test{"2", "A", | |
| "-2"}; | |
| for (std::size_t i = 0; i < int_invalid_argument_base_2_test.size(); ++i) { | |
| auto const &f = int_invalid_argument_base_2_test[i]; | |
| int result; | |
| auto answer = | |
| fast_float::from_chars(f.data(), f.data() + f.size(), result, 2); | |
| if (answer.ec != std::errc::invalid_argument) { | |
| std::cerr << "expected error should be 'invalid_argument' for: \"" << f | |
| << "\"" << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // unsigned invalid error base 2 test | |
| std::vector<std::string_view> const 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) { | |
| auto const &f = unsigned_invalid_argument_base_2_test[i]; | |
| unsigned result; | |
| auto answer = | |
| fast_float::from_chars(f.data(), f.data() + f.size(), result, 2); | |
| if (answer.ec != std::errc::invalid_argument) { | |
| std::cerr << "expected error should be 'invalid_argument' for: \"" << f | |
| << "\"" << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // octal test | |
| std::vector<int> const base_octal_test_expected{0, 1, 7, 8, 9}; | |
| std::vector<std::string_view> const base_octal_test{"0", "1", "07", "010", | |
| "0011"}; | |
| for (std::size_t i = 0; i < base_octal_test.size(); ++i) { | |
| auto const &f = base_octal_test[i]; | |
| int result; | |
| auto answer = | |
| fast_float::from_chars(f.data(), f.data() + f.size(), result, 8); | |
| if (answer.ec != std::errc()) { | |
| std::cerr << "could not convert to int for input: \"" << f << "\"" | |
| << std::endl; | |
| return EXIT_FAILURE; | |
| } else if (result != base_octal_test_expected[i]) { | |
| std::cerr << "result " << f << " did not match with expected int: " | |
| << base_octal_test_expected[i] << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // hex test | |
| std::vector<int> const base_hex_test_expected{0, 1, 15, 31, 0, 16}; | |
| std::vector<std::string_view> const base_hex_test{"0", "1", "F", | |
| "01f", "0x11", "10X11"}; | |
| for (std::size_t i = 0; i < base_hex_test.size(); ++i) { | |
| auto const &f = base_hex_test[i]; | |
| int result; | |
| auto answer = | |
| fast_float::from_chars(f.data(), f.data() + f.size(), result, 16); | |
| if (answer.ec != std::errc()) { | |
| std::cerr << "could not convert to int for input: \"" << f << "\"" | |
| << std::endl; | |
| return EXIT_FAILURE; | |
| } else if (result != base_hex_test_expected[i]) { | |
| std::cerr << "result " << f << " did not match with expected int: " | |
| << base_hex_test_expected[i] << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // invalid base test #1 (-1) | |
| std::vector<std::string_view> const invalid_base_test_1{"0", "1", "-1", "F", | |
| "10Z"}; | |
| for (std::size_t i = 0; i < invalid_base_test_1.size(); ++i) { | |
| auto const &f = invalid_base_test_1[i]; | |
| int result; | |
| auto answer = | |
| fast_float::from_chars(f.data(), f.data() + f.size(), result, -1); | |
| if (answer.ec != std::errc::invalid_argument) { | |
| std::cerr << "expected error should be 'invalid_argument' for: \"" << f | |
| << "\"" << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // invalid base test #2 (37) | |
| std::vector<std::string_view> const invalid_base_test_2{"0", "1", "F", "Z", | |
| "10Z"}; | |
| for (std::size_t i = 0; i < invalid_base_test_2.size(); ++i) { | |
| auto const &f = invalid_base_test_2[i]; | |
| int result; | |
| auto answer = | |
| fast_float::from_chars(f.data(), f.data() + f.size(), result, 37); | |
| if (answer.ec != std::errc::invalid_argument) { | |
| std::cerr << "expected error should be 'invalid_argument' for: \"" << f | |
| << "\"" << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // int out of range error base test (64 bit) | |
| std::vector<std::string_view> const int_out_of_range_base_test{ | |
| "1000000000000000000000000000000000000000000000000000000000000000", | |
| "-1000000000000000000000000000000000000000000000000000000000000001", | |
| "2021110011022210012102010021220101220222", | |
| "-2021110011022210012102010021220101221000", | |
| "20000000000000000000000000000000", | |
| "-20000000000000000000000000000001", | |
| "1104332401304422434310311213", | |
| "-1104332401304422434310311214", | |
| "1540241003031030222122212", | |
| "-1540241003031030222122213", | |
| "22341010611245052052301", | |
| "-22341010611245052052302", | |
| "1000000000000000000000", | |
| "-1000000000000000000001", | |
| "67404283172107811828", | |
| "-67404283172107811830", | |
| "9223372036854775808", | |
| "-9223372036854775809", | |
| "1728002635214590698", | |
| "-1728002635214590699", | |
| "41A792678515120368", | |
| "-41A792678515120369", | |
| "10B269549075433C38", | |
| "-10B269549075433C39", | |
| "4340724C6C71DC7A8", | |
| "-4340724C6C71DC7A9", | |
| "160E2AD3246366808", | |
| "-160E2AD3246366809", | |
| "8000000000000000", | |
| "-8000000000000001", | |
| "33D3D8307B214009", | |
| "-33D3D8307B21400A", | |
| "16AGH595DF825FA8", | |
| "-16AGH595DF825FA9", | |
| "BA643DCI0FFEEHI", | |
| "-BA643DCI0FFEEI0", | |
| "5CBFJIA3FH26JA8", | |
| "-5CBFJIA3FH26JA9", | |
| "2HEICIIIE82DH98", | |
| "-2HEICIIIE82DH99", | |
| "1ADAIBB21DCKFA8", | |
| "-1ADAIBB21DCKFA9", | |
| "I6K448CF4192C3", | |
| "-I6K448CF4192C4", | |
| "ACD772JNC9L0L8", | |
| "-ACD772JNC9L0L9", | |
| "64IE1FOCNN5G78", | |
| "-64IE1FOCNN5G79", | |
| "3IGOECJBMCA688", | |
| "-3IGOECJBMCA689", | |
| "27C48L5B37OAOQ", | |
| "-27C48L5B37OAP0", | |
| "1BK39F3AH3DMQ8", | |
| "-1BK39F3AH3DMQ9", | |
| "Q1SE8F0M04ISC", | |
| "-Q1SE8F0M04ISD", | |
| "HAJPPBC1FC208", | |
| "-HAJPPBC1FC209", | |
| "BM03I95HIA438", | |
| "-BM03I95HIA439", | |
| "8000000000000", | |
| "-8000000000001", | |
| "5HG4CK9JD4U38", | |
| "-5HG4CK9JD4U39", | |
| "3TDTK1V8J6TPQ", | |
| "-3TDTK1V8J6TPR", | |
| "2PIJMIKEXRXP8", | |
| "-2PIJMIKEXRXP9", | |
| "1Y2P0IJ32E8E8", | |
| "-1Y2P0IJ32E8E9"}; | |
| for (std::size_t i = 0; i < int_out_of_range_base_test.size(); ++i) { | |
| auto const &f = int_out_of_range_base_test[i]; | |
| int64_t result; | |
| 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) { | |
| std::cerr << "expected error for should be 'result_out_of_range': \"" << f | |
| << "\"" << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // unsigned out of range error base test (64 bit) | |
| std::vector<std::string_view> const unsigned_out_of_range_base_test{ | |
| "10000000000000000000000000000000000000000000000000000000000000000", | |
| "11112220022122120101211020120210210211221", | |
| "100000000000000000000000000000000", | |
| "2214220303114400424121122431", | |
| "3520522010102100444244424", | |
| "45012021522523134134602", | |
| "2000000000000000000000", | |
| "145808576354216723757", | |
| "18446744073709551616", | |
| "335500516A429071285", | |
| "839365134A2A240714", | |
| "219505A9511A867B73", | |
| "8681049ADB03DB172", | |
| "2C1D56B648C6CD111", | |
| "10000000000000000", | |
| "67979G60F5428011", | |
| "2D3FGB0B9CG4BD2G", | |
| "141C8786H1CCAAGH", | |
| "B53BJH07BE4DJ0G", | |
| "5E8G4GGG7G56DIG", | |
| "2L4LF104353J8KG", | |
| "1DDH88H2782I516", | |
| "L12EE5FN0JI1IG", | |
| "C9C336O0MLB7EG", | |
| "7B7N2PCNIOKCGG", | |
| "4EO8HFAM6FLLMP", | |
| "2NC6J26L66RHOG", | |
| "1N3RSH11F098RO", | |
| "14L9LKMO30O40G", | |
| "ND075IB45K86G", | |
| "G000000000000", | |
| "B1W8P7J5Q9R6G", | |
| "7ORP63SH4DPHI", | |
| "5G24A25TWKWFG", | |
| "3W5E11264SGSG"}; | |
| int base_unsigned = 2; | |
| for (std::size_t i = 0; i < unsigned_out_of_range_base_test.size(); ++i) { | |
| auto const &f = unsigned_out_of_range_base_test[i]; | |
| uint64_t result; | |
| auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, | |
| base_unsigned); | |
| if (answer.ec != std::errc::result_out_of_range) { | |
| std::cerr << "expected error for should be 'result_out_of_range': \"" << f | |
| << "\"" << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| ++base_unsigned; | |
| } | |
| // unsigned out of range error base test, multi-wrap (64 bit) | |
| // These values overflow uint64_t, but the accumulator wraps a whole multiple | |
| // of 2^64 and lands back at or above the smallest max_digits-length value, so | |
| // a single comparison against that bound does not catch the overflow. Bases | |
| // 2, 4 and 16 are excluded because their max_digits-length range fits within | |
| // a single 2^64 span. | |
| std::vector<int> const unsigned_multiwrap_base{ | |
| 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, | |
| 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36}; | |
| std::vector<std::string_view> const unsigned_multiwrap_base_test{ | |
| "22222222222222222222222222222222222222222", | |
| "4400000000000000000000000000", | |
| "5555555555555555555555555", | |
| "66666666666666666666666", | |
| "7777777777777777777777", | |
| "888888888888888888888", | |
| "46893488147419103233", | |
| "AAAAAAAAAAAAAAAAAAA", | |
| "BBBBBBBBBBBBBBBBBB", | |
| "427772311192C9BAAB", | |
| "DDDDDDDDDDDDDDDDD", | |
| "532C82996D3A44919", | |
| "GGGGGGGGGGGGGGGG", | |
| "HHHHHHHHHHHHHHHH", | |
| "3835GEGDF36622EG", | |
| "JJJJJJJJJJJJJJJ", | |
| "KKKKKKKKKKKKKKK", | |
| "LLLLLLLLLLLLLLL", | |
| "444BGHB4EG5DA2D", | |
| "NNNNNNNNNNNNNN", | |
| "JE5H4MNDLJGNLO", | |
| "PPPPPPPPPPPPPP", | |
| "QQQQQQQQQQQQQQ", | |
| "RRRRRRRRRRRRRR", | |
| "4H7QS52310IHQK", | |
| "TTTTTTTTTTTTTT", | |
| "UUUUUUUUUUUUU", | |
| "VVVVVVVVVVVVV", | |
| "WWWWWWWWWWWWW", | |
| "XXXXXXXXXXXXX", | |
| "YYYYYYYYYYYYY", | |
| "6U831JL976P6O"}; | |
| for (std::size_t i = 0; i < unsigned_multiwrap_base_test.size(); ++i) { | |
| auto const &f = unsigned_multiwrap_base_test[i]; | |
| uint64_t result; | |
| auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, | |
| unsigned_multiwrap_base[i]); | |
| if (answer.ec != std::errc::result_out_of_range) { | |
| std::cerr << "expected error for should be 'result_out_of_range': \"" << f | |
| << "\"" << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // just within range base test (64 bit) | |
| std::vector<std::string_view> const int_within_range_base_test{ | |
| "111111111111111111111111111111111111111111111111111111111111111", | |
| "-1000000000000000000000000000000000000000000000000000000000000000", | |
| "2021110011022210012102010021220101220221", | |
| "-2021110011022210012102010021220101220222", | |
| "13333333333333333333333333333333", | |
| "-20000000000000000000000000000000", | |
| "1104332401304422434310311212", | |
| "-1104332401304422434310311213", | |
| "1540241003031030222122211", | |
| "-1540241003031030222122212", | |
| "22341010611245052052300", | |
| "-22341010611245052052301", | |
| "777777777777777777777", | |
| "-1000000000000000000000", | |
| "67404283172107811827", | |
| "-67404283172107811828", | |
| "9223372036854775807", | |
| "-9223372036854775808", | |
| "1728002635214590697", | |
| "-1728002635214590698", | |
| "41A792678515120367", | |
| "-41A792678515120368", | |
| "10B269549075433C37", | |
| "-10B269549075433C38", | |
| "4340724C6C71DC7A7", | |
| "-4340724C6C71DC7A8", | |
| "160E2AD3246366807", | |
| "-160E2AD3246366808", | |
| "7FFFFFFFFFFFFFFF", | |
| "-8000000000000000", | |
| "33D3D8307B214008", | |
| "-33D3D8307B214009", | |
| "16AGH595DF825FA7", | |
| "-16AGH595DF825FA8", | |
| "BA643DCI0FFEEHH", | |
| "-BA643DCI0FFEEHI", | |
| "5CBFJIA3FH26JA7", | |
| "-5CBFJIA3FH26JA8", | |
| "2HEICIIIE82DH97", | |
| "-2HEICIIIE82DH98", | |
| "1ADAIBB21DCKFA7", | |
| "-1ADAIBB21DCKFA8", | |
| "I6K448CF4192C2", | |
| "-I6K448CF4192C3", | |
| "ACD772JNC9L0L7", | |
| "-ACD772JNC9L0L8", | |
| "64IE1FOCNN5G77", | |
| "-64IE1FOCNN5G78", | |
| "3IGOECJBMCA687", | |
| "-3IGOECJBMCA688", | |
| "27C48L5B37OAOP", | |
| "-27C48L5B37OAOQ", | |
| "1BK39F3AH3DMQ7", | |
| "-1BK39F3AH3DMQ8", | |
| "Q1SE8F0M04ISB", | |
| "-Q1SE8F0M04ISC", | |
| "HAJPPBC1FC207", | |
| "-HAJPPBC1FC208", | |
| "BM03I95HIA437", | |
| "-BM03I95HIA438", | |
| "7VVVVVVVVVVVV", | |
| "-8000000000000", | |
| "5HG4CK9JD4U37", | |
| "-5HG4CK9JD4U38", | |
| "3TDTK1V8J6TPP", | |
| "-3TDTK1V8J6TPQ", | |
| "2PIJMIKEXRXP7", | |
| "-2PIJMIKEXRXP8", | |
| "1Y2P0IJ32E8E7", | |
| "-1Y2P0IJ32E8E8"}; | |
| for (std::size_t i = 0; i < int_within_range_base_test.size(); ++i) { | |
| auto const &f = int_within_range_base_test[i]; | |
| int64_t result; | |
| auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, | |
| int(2 + (i / 2))); | |
| if (answer.ec != std::errc()) { | |
| std::cerr << "converting " << f | |
| << " to int failed (most likely out of range)" << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // unsigned within range base test (64 bit) | |
| std::vector<std::string_view> const unsigned_within_range_base_test{ | |
| "1111111111111111111111111111111111111111111111111111111111111111", | |
| "11112220022122120101211020120210210211220", | |
| "33333333333333333333333333333333", | |
| "2214220303114400424121122430", | |
| "3520522010102100444244423", | |
| "45012021522523134134601", | |
| "1777777777777777777777", | |
| "145808576354216723756", | |
| "18446744073709551615", | |
| "335500516A429071284", | |
| "839365134A2A240713", | |
| "219505A9511A867B72", | |
| "8681049ADB03DB171", | |
| "2C1D56B648C6CD110", | |
| "FFFFFFFFFFFFFFFF", | |
| "67979G60F5428010", | |
| "2D3FGB0B9CG4BD2F", | |
| "141C8786H1CCAAGG", | |
| "B53BJH07BE4DJ0F", | |
| "5E8G4GGG7G56DIF", | |
| "2L4LF104353J8KF", | |
| "1DDH88H2782I515", | |
| "L12EE5FN0JI1IF", | |
| "C9C336O0MLB7EF", | |
| "7B7N2PCNIOKCGF", | |
| "4EO8HFAM6FLLMO", | |
| "2NC6J26L66RHOF", | |
| "1N3RSH11F098RN", | |
| "14L9LKMO30O40F", | |
| "ND075IB45K86F", | |
| "FVVVVVVVVVVVV", | |
| "B1W8P7J5Q9R6F", | |
| "7ORP63SH4DPHH", | |
| "5G24A25TWKWFF", | |
| "3W5E11264SGSF"}; | |
| int base_unsigned2 = 2; | |
| for (std::size_t i = 0; i < unsigned_within_range_base_test.size(); ++i) { | |
| auto const &f = unsigned_within_range_base_test[i]; | |
| uint64_t result; | |
| auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, | |
| base_unsigned2); | |
| if (answer.ec != std::errc()) { | |
| std::cerr << "converting " << f | |
| << " to unsigned failed (most likely out of range)" | |
| << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| ++base_unsigned2; | |
| } | |
| // int leading zeros test | |
| std::vector<std::string_view> const int_leading_zeros_test{ | |
| "000000000000000000000000000000000000000000000000000000000000000000000011" | |
| "11110111", | |
| "000000000000000000000000000000000000000000000000001101121", | |
| "000000000000000000000000000000000000000033313", | |
| "00000000000000000000000000000013030", | |
| "0000000000000000000000000000004411", | |
| "0000000000000000000000000000002650", | |
| "0000000000000000000000000000001767", | |
| "0000000000000000000000000000001347", | |
| "0000000000000000000000000000001015", | |
| "00000000000000000000843", | |
| "00000000000000000000707", | |
| "00000000000000000000601", | |
| "00000000000000000000527", | |
| "0000000000000000000047A", | |
| "000000000000000000003F7", | |
| "0000000000000000000038C", | |
| "00000000000000000000327", | |
| "000000000000000000002F8", | |
| "000000000000000000002AF", | |
| "00000000000000000000267", | |
| "00000000000000000000223", | |
| "000000000000000000001L3", | |
| "000000000000000000001I7", | |
| "000000000000000000001FF", | |
| "000000000000000000001D1", | |
| "000000000000000000001AG", | |
| "00000000000000000000187", | |
| "00000000000000000000160", | |
| "0000000000000000000013P", | |
| "0000000000000000000011N", | |
| "00000000000000000000VN", | |
| "00000000000000000000UP", | |
| "00000000000000000000TT", | |
| "00000000000000000000T0", | |
| "00000000000000000000S7"}; | |
| for (std::size_t i = 0; i < int_leading_zeros_test.size(); ++i) { | |
| auto const &f = int_leading_zeros_test[i]; | |
| int result; | |
| auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, | |
| int(i + 2)); | |
| if (answer.ec != std::errc()) { | |
| std::cerr << "could not convert to int for input: \"" << f << "\"" | |
| << std::endl; | |
| return EXIT_FAILURE; | |
| } else if (result != 1015) { | |
| std::cerr << "result " << 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; | |
| } | |
| } | |
| // dont parse UTF-16 code units of emojis as int if low byte is ascii digit | |
| { | |
| const std::u16string emojis[] = { | |
| u"ℹ", u"ℹ️", u"☸", u"☸️", u"☹", u"☹️", u"✳", u"✳️", | |
| u"✴", u"✴️", u"⤴", u"⤴️", u"⤵", u"⤵️", u"〰", u"〰️", | |
| }; | |
| bool failed = false; | |
| auto array_size = sizeof(emojis) / sizeof(emojis[0]); | |
| for (size_t i = 0; i < array_size; i++) { | |
| auto e = emojis[i]; | |
| int foo; | |
| auto answer = fast_float::from_chars(e.data(), e.data() + e.size(), foo); | |
| if (answer.ec == std::errc()) { | |
| failed = true; | |
| std::cerr << "Incorrectly parsed emoji #" << i << " as integer " << foo | |
| << "." << std::endl; | |
| } | |
| } | |
| if (failed) { | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // dont parse UTF-32 code points of emojis as int if low byte is ascii digit | |
| { | |
| const std::u32string emojis[] = { | |
| U"ℹ", | |
| U"ℹ️", | |
| U"☸", | |
| U"☸️", | |
| U"☹", | |
| U"☹️", | |
| U"✳", | |
| U"✳️", | |
| U"✴", | |
| U"✴️", | |
| U"⤴", | |
| U"⤴️", | |
| U"⤵", | |
| U"⤵️", | |
| U"〰", | |
| U"〰️", | |
| U"🈲", | |
| U"🈳", | |
| U"🈴", | |
| U"🈵", | |
| U"🈶", | |
| U"🈷", | |
| U"🈷️", | |
| U"🈸", | |
| U"🈹", | |
| U"🌰", | |
| U"🌱", | |
| U"🌲", | |
| U"🌳", | |
| U"🌴", | |
| U"🌵", | |
| U"🌶", | |
| U"🌶️", | |
| U"🌷", | |
| U"🌸", | |
| U"🌹", | |
| U"🐰", | |
| U"🐱", | |
| U"🐲", | |
| U"🐳", | |
| U"🐴", | |
| U"🐵", | |
| U"🐶", | |
| U"🐷", | |
| U"🐸", | |
| U"🐹", | |
| U"🔰", | |
| U"🔱", | |
| U"🔲", | |
| U"🔳", | |
| U"🔴", | |
| U"🔵", | |
| U"🔶", | |
| U"🔷", | |
| U"🔸", | |
| U"🔹", | |
| U"😰", | |
| U"😱", | |
| U"😲", | |
| U"😳", | |
| U"😴", | |
| U"😵", | |
| U"😵💫", | |
| U"😶", | |
| U"😶🌫", | |
| U"😶🌫️", | |
| U"😷", | |
| U"😸", | |
| U"😹", | |
| U"🤰", | |
| U"🤰🏻", | |
| U"🤰🏼", | |
| U"🤰🏽", | |
| U"🤰🏾", | |
| U"🤰🏿", | |
| U"🤱", | |
| U"🤱🏻", | |
| U"🤱🏼", | |
| U"🤱🏽", | |
| U"🤱🏾", | |
| U"🤱🏿", | |
| U"🤲", | |
| U"🤲🏻", | |
| U"🤲🏼", | |
| U"🤲🏽", | |
| U"🤲🏾", | |
| U"🤲🏿", | |
| U"🤳", | |
| U"🤳🏻", | |
| U"🤳🏼", | |
| U"🤳🏽", | |
| U"🤳🏾", | |
| U"🤳🏿", | |
| U"🤴", | |
| U"🤴🏻", | |
| U"🤴🏼", | |
| U"🤴🏽", | |
| U"🤴🏾", | |
| U"🤴🏿", | |
| U"🤵", | |
| U"🤵♀", | |
| U"🤵♀️", | |
| U"🤵♂", | |
| U"🤵♂️", | |
| U"🤵🏻", | |
| U"🤵🏻♀", | |
| U"🤵🏻♀️", | |
| U"🤵🏻♂", | |
| U"🤵🏻♂️", | |
| U"🤵🏼", | |
| U"🤵🏼♀", | |
| U"🤵🏼♀️", | |
| U"🤵🏼♂", | |
| U"🤵🏼♂️", | |
| U"🤵🏽", | |
| U"🤵🏽♀", | |
| U"🤵🏽♀️", | |
| U"🤵🏽♂", | |
| U"🤵🏽♂️", | |
| U"🤵🏾", | |
| U"🤵🏾♀", | |
| U"🤵🏾♀️", | |
| U"🤵🏾♂", | |
| U"🤵🏾♂️", | |
| U"🤵🏿", | |
| U"🤵🏿♀", | |
| U"🤵🏿♀️", | |
| U"🤵🏿♂", | |
| U"🤵🏿♂️", | |
| U"🤶", | |
| U"🤶🏻", | |
| U"🤶🏼", | |
| U"🤶🏽", | |
| U"🤶🏾", | |
| U"🤶🏿", | |
| U"🤷", | |
| U"🤷♀", | |
| U"🤷♀️", | |
| U"🤷♂", | |
| U"🤷♂️", | |
| U"🤷🏻", | |
| U"🤷🏻♀", | |
| U"🤷🏻♀️", | |
| U"🤷🏻♂", | |
| U"🤷🏻♂️", | |
| U"🤷🏼", | |
| U"🤷🏼♀", | |
| U"🤷🏼♀️", | |
| U"🤷🏼♂", | |
| U"🤷🏼♂️", | |
| U"🤷🏽", | |
| U"🤷🏽♀", | |
| U"🤷🏽♀️", | |
| U"🤷🏽♂", | |
| U"🤷🏽♂️", | |
| U"🤷🏾", | |
| U"🤷🏾♀", | |
| U"🤷🏾♀️", | |
| U"🤷🏾♂", | |
| U"🤷🏾♂️", | |
| U"🤷🏿", | |
| U"🤷🏿♀", | |
| U"🤷🏿♀️", | |
| U"🤷🏿♂", | |
| U"🤷🏿♂️", | |
| U"🤸", | |
| U"🤸♀", | |
| U"🤸♀️", | |
| U"🤸♂", | |
| U"🤸♂️", | |
| U"🤸🏻", | |
| U"🤸🏻♀", | |
| U"🤸🏻♀️", | |
| U"🤸🏻♂", | |
| U"🤸🏻♂️", | |
| U"🤸🏼", | |
| U"🤸🏼♀", | |
| U"🤸🏼♀️", | |
| U"🤸🏼♂", | |
| U"🤸🏼♂️", | |
| U"🤸🏽", | |
| U"🤸🏽♀", | |
| U"🤸🏽♀️", | |
| U"🤸🏽♂", | |
| U"🤸🏽♂️", | |
| U"🤸🏾", | |
| U"🤸🏾♀", | |
| U"🤸🏾♀️", | |
| U"🤸🏾♂", | |
| U"🤸🏾♂️", | |
| U"🤸🏿", | |
| U"🤸🏿♀", | |
| U"🤸🏿♀️", | |
| U"🤸🏿♂", | |
| U"🤸🏿♂️", | |
| U"🤹", | |
| U"🤹♀", | |
| U"🤹♀️", | |
| U"🤹♂", | |
| U"🤹♂️", | |
| U"🤹🏻", | |
| U"🤹🏻♀", | |
| U"🤹🏻♀️", | |
| U"🤹🏻♂", | |
| U"🤹🏻♂️", | |
| U"🤹🏼", | |
| U"🤹🏼♀", | |
| U"🤹🏼♀️", | |
| U"🤹🏼♂", | |
| U"🤹🏼♂️", | |
| U"🤹🏽", | |
| U"🤹🏽♀", | |
| U"🤹🏽♀️", | |
| U"🤹🏽♂", | |
| U"🤹🏽♂️", | |
| U"🤹🏾", | |
| U"🤹🏾♀", | |
| U"🤹🏾♀️", | |
| U"🤹🏾♂", | |
| U"🤹🏾♂️", | |
| U"🤹🏿", | |
| U"🤹🏿♀", | |
| U"🤹🏿♀️", | |
| U"🤹🏿♂", | |
| U"🤹🏿♂️", | |
| }; | |
| bool failed = false; | |
| auto array_size = sizeof(emojis) / sizeof(emojis[0]); | |
| for (size_t i = 0; i < array_size; i++) { | |
| auto e = emojis[i]; | |
| int foo; | |
| auto answer = fast_float::from_chars(e.data(), e.data() + e.size(), foo); | |
| if (answer.ec == std::errc()) { | |
| failed = true; | |
| std::cerr << "Incorrectly parsed emoji #" << i << " as integer " << foo | |
| << "." << std::endl; | |
| } | |
| } | |
| if (failed) { | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // The uint8_t and uint16_t base-10 paths use a byte-oriented fast path. A | |
| // wider code unit whose low byte is an ASCII digit (e.g. U+2131..U+2139) must | |
| // not be mistaken for that digit. The generic path already rejects these for | |
| // int; the fixed-width fast paths must agree. Lengths of 1..5 exercise both | |
| // the uint8_t path and the 4-digit uint16_t SWAR path. | |
| { | |
| const std::u16string bad16[] = { | |
| u"ℹ", u"ℱℲ", u"ℱℲℳ", u"ℱℲℳℴ", u"ℱℲℳℴℵ", | |
| }; | |
| const std::u32string bad32[] = { | |
| U"ℹ", | |
| U"ℱℲℳ", | |
| U"ℱℲℳℴ", | |
| U"ℱℲℳℴℵ", | |
| }; | |
| bool failed = false; | |
| for (auto const &s : bad16) { | |
| uint8_t r8 = 123; | |
| auto a8 = fast_float::from_chars(s.data(), s.data() + s.size(), r8); | |
| if (a8.ec == std::errc()) { | |
| failed = true; | |
| std::cerr << "Incorrectly parsed wide units as uint8_t " << unsigned(r8) | |
| << "." << std::endl; | |
| } | |
| uint16_t r16 = 123; | |
| auto a16 = fast_float::from_chars(s.data(), s.data() + s.size(), r16); | |
| if (a16.ec == std::errc()) { | |
| failed = true; | |
| std::cerr << "Incorrectly parsed wide units as uint16_t " << r16 << "." | |
| << std::endl; | |
| } | |
| } | |
| for (auto const &s : bad32) { | |
| uint8_t r8 = 123; | |
| auto a8 = fast_float::from_chars(s.data(), s.data() + s.size(), r8); | |
| if (a8.ec == std::errc()) { | |
| failed = true; | |
| std::cerr << "Incorrectly parsed wide units as uint8_t " << unsigned(r8) | |
| << "." << std::endl; | |
| } | |
| uint16_t r16 = 123; | |
| auto a16 = fast_float::from_chars(s.data(), s.data() + s.size(), r16); | |
| if (a16.ec == std::errc()) { | |
| failed = true; | |
| std::cerr << "Incorrectly parsed wide units as uint16_t " << r16 << "." | |
| << std::endl; | |
| } | |
| } | |
| if (failed) { | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // Comprehensive, oracle-checked u64 overflow detection across every base. | |
| // | |
| // The accumulator in parse_int_string is allowed to overflow and the result | |
| // is validated afterwards. At the max_digits boundary a value can wrap one or | |
| // more whole multiples of 2^64 (a 20-digit base-10 number reaches ~5.4*2^64), | |
| // so the boundary check must be exact. This section validates from_chars for | |
| // bases 2..36 against an independent, trusted oracle: a plain 64-bit checked | |
| // multiply-add. It hammers the single leading-digit band that straddles 2^64 | |
| // (where wrapped and non-wrapped values are hardest to tell apart) and also | |
| // covers max_digits-1 (always in range) and max_digits+1 (always overflow). | |
| { | |
| auto digit_to_char = [](int d) -> char { | |
| return d < 10 ? char('0' + d) : char('A' + (d - 10)); | |
| }; | |
| auto char_to_digit = [](char c) -> int { | |
| if (c >= '0' && c <= '9') { | |
| return c - '0'; | |
| } | |
| if (c >= 'A' && c <= 'Z') { | |
| return c - 'A' + 10; | |
| } | |
| return c - 'a' + 10; | |
| }; | |
| // Trusted oracle: parse `s` in `base` with a checked 64-bit multiply-add. | |
| // Returns true on u64 overflow; otherwise writes the value to `out`. | |
| auto oracle = [&](std::string const &s, int base, uint64_t &out) -> bool { | |
| uint64_t v = 0; | |
| for (char c : s) { | |
| uint64_t const d = uint64_t(char_to_digit(c)); | |
| if (v > (UINT64_MAX - d) / uint64_t(base)) { | |
| return true; | |
| } | |
| v = uint64_t(base) * v + d; | |
| } | |
| out = v; | |
| return false; | |
| }; | |
| auto to_base = [&](uint64_t v, int base) -> std::string { | |
| if (v == 0) { | |
| return "0"; | |
| } | |
| std::string s; | |
| while (v != 0) { | |
| s += digit_to_char(int(v % uint64_t(base))); | |
| v /= uint64_t(base); | |
| } | |
| std::reverse(s.begin(), s.end()); | |
| return s; | |
| }; | |
| // Add one (in base `base`) to the digit string `s`, carrying as needed. | |
| auto increment = [&](std::string s, int base) -> std::string { | |
| int carry = 1; | |
| for (std::size_t k = s.size(); k-- > 0 && carry != 0;) { | |
| int const d = char_to_digit(s[k]) + carry; | |
| carry = d / base; | |
| s[k] = digit_to_char(d % base); | |
| } | |
| if (carry != 0) { | |
| s.insert(s.begin(), digit_to_char(carry)); | |
| } | |
| return s; | |
| }; | |
| // Subtract one (in base `base`) from a non-zero, non-negative string. | |
| auto decrement = [&](std::string s, int base) -> std::string { | |
| int borrow = 1; | |
| for (std::size_t k = s.size(); k-- > 0 && borrow != 0;) { | |
| int d = char_to_digit(s[k]) - borrow; | |
| borrow = d < 0 ? 1 : 0; | |
| if (d < 0) { | |
| d += base; | |
| } | |
| s[k] = digit_to_char(d); | |
| } | |
| std::size_t lead = s.find_first_not_of('0'); // drop any leading zero | |
| return lead == std::string::npos ? "0" : s.substr(lead); | |
| }; | |
| std::mt19937_64 rng(0xC0FFEEULL); | |
| long long checked = 0; | |
| auto verify = [&](std::string const &s, int base) -> bool { | |
| uint64_t expected = 0; | |
| bool const ov = oracle(s, base, expected); | |
| uint64_t result = 0xDEADBEEFULL; | |
| auto answer = | |
| fast_float::from_chars(s.data(), s.data() + s.size(), result, base); | |
| ++checked; | |
| if (ov) { | |
| if (answer.ec != std::errc::result_out_of_range) { | |
| std::cerr << "base " << base | |
| << ": expected result_out_of_range for \"" << s << "\"" | |
| << std::endl; | |
| return false; | |
| } | |
| } else { | |
| if (answer.ec != std::errc()) { | |
| std::cerr << "base " << base << ": unexpected error for \"" << s | |
| << "\"" << std::endl; | |
| return false; | |
| } | |
| if (result != expected) { | |
| std::cerr << "base " << base << ": \"" << s << "\" -> " << result | |
| << ", expected " << expected << std::endl; | |
| return false; | |
| } | |
| if (answer.ptr != s.data() + s.size()) { | |
| std::cerr << "base " << base << ": did not consume all of \"" << s | |
| << "\"" << std::endl; | |
| return false; | |
| } | |
| } | |
| return true; | |
| }; | |
| // Leading zeros are stripped before the digit count, so the outcome must be | |
| // unchanged. Checked only on hand-picked values (it exercises shared code). | |
| auto verify_zeros = [&](std::string const &digits, int base) -> bool { | |
| return verify(digits, base) && verify("0" + digits, base) && | |
| verify(std::string(40, '0') + digits, base); | |
| }; | |
| auto random_tail = [&](std::string &s, int n, int base) { | |
| for (int k = 0; k < n; ++k) { | |
| // bias toward the extremes (0 and base-1) to hit boundaries often | |
| std::uint64_t const r = rng(); | |
| int const mode = int(r % 4); | |
| int const dig = mode == 0 ? 0 | |
| : mode == 1 ? base - 1 | |
| : int((r >> 2) % std::uint64_t(base)); | |
| s += digit_to_char(dig); | |
| } | |
| }; | |
| for (int base = 2; base <= 36; ++base) { | |
| // M = max number of base-`base` digits a u64 can hold. | |
| std::string const maxstr = to_base(UINT64_MAX, base); | |
| int const M = int(maxstr.size()); | |
| // b^(M-1): smallest M-digit value, and width of each leading-digit band. | |
| uint64_t bM1 = 1; | |
| for (int k = 0; k < M - 1; ++k) { | |
| bM1 *= uint64_t(base); | |
| } | |
| int const dmax = int(UINT64_MAX / bM1); // largest leading digit that fits | |
| // Exact-boundary sweep straddling 2^64 (the hardest transition): the | |
| // 64 values UINT64_MAX-31 .. UINT64_MAX (in range) and 2^64 .. 2^64+31 | |
| // (overflow), built by walking the digit string up and down. | |
| std::string below = maxstr, above = increment(maxstr, base); | |
| for (int k = 0; k < 32; ++k) { | |
| if (!verify(below, base) || !verify(above, base)) { | |
| return EXIT_FAILURE; | |
| } | |
| below = decrement(below, base); | |
| above = increment(above, base); | |
| } | |
| // Hand-picked values, also checked with leading zeros. | |
| std::string const allmax(std::size_t(M), digit_to_char(base - 1)); | |
| if (!verify_zeros(maxstr, base) || // largest in-range value | |
| !verify_zeros(increment(maxstr, base), base) || // smallest overflow | |
| !verify_zeros(allmax, base)) { // largest M-digit (multi-wrap) | |
| return EXIT_FAILURE; | |
| } | |
| // Randomized M-digit values across every leading digit. Bands with | |
| // lead > dmax always overflow (this is where the naive min_safe check | |
| // wrongly accepted multi-wrap values); lead < dmax always fits; lead == | |
| // dmax straddles 2^64 and gets the heaviest sampling. | |
| for (int lead = 1; lead < base; ++lead) { | |
| int const trials = lead == dmax ? 4000 : 300; | |
| for (int trial = 0; trial < trials; ++trial) { | |
| std::string s(1, digit_to_char(lead)); | |
| random_tail(s, M - 1, base); | |
| if (!verify(s, base)) { | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| } | |
| // max_digits-1 digits never overflow; max_digits+1 digits always do. | |
| for (int trial = 0; trial < 500; ++trial) { | |
| std::string shorts(1, | |
| digit_to_char(1 + int(rng() % uint64_t(base - 1)))); | |
| random_tail(shorts, M - 2, base); | |
| std::string longs(1, | |
| digit_to_char(1 + int(rng() % uint64_t(base - 1)))); | |
| random_tail(longs, M, base); | |
| if (!verify(shorts, base) || !verify(longs, base)) { | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| } | |
| if (checked < 100000) { | |
| std::cerr << "overflow sweep ran too few cases: " << checked << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // Signed (int64_t) boundary: every value that overflows u64 also overflows | |
| // i64, and the exact i64 limits must parse. Reuses the oracle indirectly via | |
| // hand-built extremes per base. | |
| { | |
| auto digit_to_char = [](int d) -> char { | |
| return d < 10 ? char('0' + d) : char('A' + (d - 10)); | |
| }; | |
| auto to_base_signed = [&](int64_t value, int base) -> std::string { | |
| // value may be INT64_MIN; accumulate magnitude in u64 to avoid UB. | |
| bool const neg = value < 0; | |
| uint64_t mag = neg ? (~uint64_t(value) + 1) : uint64_t(value); | |
| std::string s; | |
| if (mag == 0) { | |
| s += '0'; | |
| } | |
| while (mag != 0) { | |
| s += digit_to_char(int(mag % uint64_t(base))); | |
| mag /= uint64_t(base); | |
| } | |
| if (neg) { | |
| s += '-'; | |
| } | |
| std::reverse(s.begin(), s.end()); | |
| return s; | |
| }; | |
| for (int base = 2; base <= 36; ++base) { | |
| struct { | |
| int64_t v; | |
| } const limits[] = {{INT64_MAX}, {INT64_MIN}, {0}, {-1}, {1}}; | |
| for (auto const &lim : limits) { | |
| std::string const s = to_base_signed(lim.v, base); | |
| int64_t result = 123; | |
| auto answer = | |
| fast_float::from_chars(s.data(), s.data() + s.size(), result, base); | |
| if (answer.ec != std::errc() || result != lim.v) { | |
| std::cerr << "base " << base << ": signed limit \"" << s | |
| << "\" failed to round-trip (got " << result << ")" | |
| << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| // Increment a non-negative magnitude string (in `base`) by one. | |
| auto inc_mag = [&](std::string m) -> std::string { | |
| int carry = 1; | |
| for (std::size_t k = m.size(); k-- > 0 && carry != 0;) { | |
| int d = (m[k] >= '0' && m[k] <= '9') ? m[k] - '0' | |
| : (m[k] >= 'A' && m[k] <= 'Z') ? m[k] - 'A' + 10 | |
| : m[k] - 'a' + 10; | |
| d += carry; | |
| carry = d / base; | |
| m[k] = digit_to_char(d % base); | |
| } | |
| if (carry != 0) { | |
| m.insert(m.begin(), digit_to_char(carry)); | |
| } | |
| return m; | |
| }; | |
| // INT64_MAX + 1 (= 2^63) overflows a positive int64_t. | |
| // INT64_MIN - 1 (= -(2^63 + 1)) overflows a negative int64_t. | |
| // Note that -(2^63) == INT64_MIN is in range and is covered above. | |
| std::string const max_mag = to_base_signed(INT64_MAX, base); // 2^63 - 1 | |
| std::string const over = inc_mag(max_mag); // 2^63 | |
| std::string const under = "-" + inc_mag(over); // -(2^63 + 1) | |
| for (std::string const &s : {over, under}) { | |
| int64_t result = 123; | |
| auto answer = | |
| fast_float::from_chars(s.data(), s.data() + s.size(), result, base); | |
| if (answer.ec != std::errc::result_out_of_range) { | |
| std::cerr << "base " << base << ": expected result_out_of_range for " | |
| << "signed \"" << s << "\"" << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| } | |
| } | |
| } | |
| return EXIT_SUCCESS; | |
| } | |
| #else | |
| #include <iostream> | |
| #include <cstdlib> | |
| int main() { | |
| std::cerr << "The test requires C++17." << std::endl; | |
| return EXIT_SUCCESS; | |
| } | |
| #endif |