mirror of
https://github.com/fastfloat/fast_float.git
synced 2026-06-15 00:16:11 +08:00
1692 lines
59 KiB
C++
1692 lines
59 KiB
C++
#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
|