Merge branch 'main' into main

This commit is contained in:
Daniel Lemire 2024-01-28 10:09:56 -05:00 committed by GitHub
commit 620376ad39
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 1044 additions and 34 deletions

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.9) cmake_minimum_required(VERSION 3.9)
project(fast_float VERSION 5.3.0 LANGUAGES CXX) project(fast_float VERSION 6.0.0 LANGUAGES CXX)
option(FASTFLOAT_TEST "Enable tests" OFF) option(FASTFLOAT_TEST "Enable tests" OFF)
if(FASTFLOAT_TEST) if(FASTFLOAT_TEST)
enable_testing() enable_testing()

View File

@ -7,4 +7,4 @@ Fabio Pellacini
Lénárd Szolnoki Lénárd Szolnoki
Jan Pharago Jan Pharago
Maya Warrier Maya Warrier
Taha Khokhar Taha Khokhar

View File

@ -4,17 +4,21 @@
[![Ubuntu 22.04 CI (GCC 11)](https://github.com/fastfloat/fast_float/actions/workflows/ubuntu22.yml/badge.svg)](https://github.com/fastfloat/fast_float/actions/workflows/ubuntu22.yml) [![Ubuntu 22.04 CI (GCC 11)](https://github.com/fastfloat/fast_float/actions/workflows/ubuntu22.yml/badge.svg)](https://github.com/fastfloat/fast_float/actions/workflows/ubuntu22.yml)
The fast_float library provides fast header-only implementations for the C++ from_chars The fast_float library provides fast header-only implementations for the C++ from_chars
functions for `float` and `double` types. These functions convert ASCII strings representing functions for `float` and `double` types as well as integer types. These functions convert ASCII strings representing decimal values (e.g., `1.3e10`) into binary types. We provide exact rounding (including
decimal values (e.g., `1.3e10`) into binary types. We provide exact rounding (including
round to even). In our experience, these `fast_float` functions many times faster than comparable number-parsing functions from existing C++ standard libraries. round to even). In our experience, these `fast_float` functions many times faster than comparable number-parsing functions from existing C++ standard libraries.
Specifically, `fast_float` provides the following two functions with a C++17-like syntax (the library itself only requires C++11): Specifically, `fast_float` provides the following two functions to parse floating-point numbers with a C++17-like syntax (the library itself only requires C++11):
```C++ ```C++
from_chars_result from_chars(const char* first, const char* last, float& value, ...); from_chars_result from_chars(const char* first, const char* last, float& value, ...);
from_chars_result from_chars(const char* first, const char* last, double& value, ...); from_chars_result from_chars(const char* first, const char* last, double& value, ...);
``` ```
You can also parse integer types:
The return type (`from_chars_result`) is defined as the struct: The return type (`from_chars_result`) is defined as the struct:
```C++ ```C++
struct from_chars_result { struct from_chars_result {
@ -103,6 +107,43 @@ We support Visual Studio, macOS, Linux, freeBSD. We support big and little endia
We assume that the rounding mode is set to nearest (`std::fegetround() == FE_TONEAREST`). We assume that the rounding mode is set to nearest (`std::fegetround() == FE_TONEAREST`).
## Integer types
You can also parse integer types using different bases (e.g., 2, 10, 16). The following code will
print the number 22250738585072012 three times:
```C++
uint64_t i;
const char str[] = "22250738585072012";
auto answer = fast_float::from_chars(str, str + strlen(str), i);
if (answer.ec != std::errc()) {
std::cerr << "parsing failure\n";
return EXIT_FAILURE;
}
std::cout << "parsed the number "<< i << std::endl;
const char binstr[] = "1001111000011001110110111001001010110100111000110001100";
answer = fast_float::from_chars(binstr, binstr + strlen(binstr), i, 2);
if (answer.ec != std::errc()) {
std::cerr << "parsing failure\n";
return EXIT_FAILURE;
}
std::cout << "parsed the number "<< i << std::endl;
const char hexstr[] = "4f0cedc95a718c";
answer = fast_float::from_chars(hexstr, hexstr + strlen(hexstr), i, 16);
if (answer.ec != std::errc()) {
std::cerr << "parsing failure\n";
return EXIT_FAILURE;
}
std::cout << "parsed the number "<< i << std::endl;
```
## C++20: compile-time evaluation (constexpr) ## C++20: compile-time evaluation (constexpr)
In C++20, you may use `fast_float::from_chars` to parse strings In C++20, you may use `fast_float::from_chars` to parse strings
@ -264,7 +305,7 @@ The fast_float library provides a performance similar to that of the [fast_doubl
## Users ## Users
The fast_float library is used by [Apache Arrow](https://github.com/apache/arrow/pull/8494) where it multiplied the number parsing speed by two or three times. It is also used by [Yandex ClickHouse](https://github.com/ClickHouse/ClickHouse) and by [Google Jsonnet](https://github.com/google/jsonnet). The fast_float library is used by [Apache Arrow](https://github.com/apache/arrow/pull/8494) where it multiplied the number parsing speed by two or three times. It is also used by [ClickHouse](https://github.com/ClickHouse/ClickHouse) and by [Google Jsonnet](https://github.com/google/jsonnet). It is part of GCC (as of GCC 12). It is part of WebKit (Safari).
## How fast is it? ## How fast is it?
@ -330,7 +371,11 @@ the command line help.
You may directly download automatically generated single-header files: You may directly download automatically generated single-header files:
https://github.com/fastfloat/fast_float/releases/download/v5.3.0/fast_float.h https://github.com/fastfloat/fast_float/releases/download/v6.0.0/fast_float.h
## RFC 7159
If you need support for RFC 7159 (JSON standard), you may want to consider using the [fast_double_parser](https://github.com/lemire/fast_double_parser/) library instead.
## Credit ## Credit

View File

@ -5,6 +5,7 @@
#include <cstdint> #include <cstdint>
#include <cstring> #include <cstring>
#include <iterator> #include <iterator>
#include <limits>
#include <type_traits> #include <type_traits>
#include "float_common.h" #include "float_common.h"
@ -115,7 +116,7 @@ FASTFLOAT_SIMD_RESTORE_WARNINGS
#if defined(_MSC_VER) && _MSC_VER <= 1900 #if defined(_MSC_VER) && _MSC_VER <= 1900
template <typename UC> template <typename UC>
#else #else
template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>())> template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>()) = 0>
#endif #endif
// dummy for compile // dummy for compile
uint64_t simd_read8_to_u64(UC const*) { uint64_t simd_read8_to_u64(UC const*) {
@ -223,7 +224,7 @@ FASTFLOAT_SIMD_RESTORE_WARNINGS
#if defined(_MSC_VER) && _MSC_VER <= 1900 #if defined(_MSC_VER) && _MSC_VER <= 1900
template <typename UC> template <typename UC>
#else #else
template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>())> template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>()) = 0>
#endif #endif
// dummy for compile // dummy for compile
bool simd_parse_if_eight_digits_unrolled(UC const*, uint64_t&) { bool simd_parse_if_eight_digits_unrolled(UC const*, uint64_t&) {
@ -231,7 +232,7 @@ bool simd_parse_if_eight_digits_unrolled(UC const*, uint64_t&) {
} }
template <typename UC, FASTFLOAT_ENABLE_IF(!std::is_same<UC, char>::value)> template <typename UC, FASTFLOAT_ENABLE_IF(!std::is_same<UC, char>::value) = 0>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 fastfloat_really_inline FASTFLOAT_CONSTEXPR20
void loop_parse_if_eight_digits(const UC*& p, const UC* const pend, uint64_t& i) { void loop_parse_if_eight_digits(const UC*& p, const UC* const pend, uint64_t& i) {
if (!has_simd_opt<UC>()) { if (!has_simd_opt<UC>()) {
@ -439,6 +440,106 @@ parsed_number_string_t<UC> parse_number_string(UC const *p, UC const * pend, par
return answer; return answer;
} }
template <typename T, typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
from_chars_result_t<UC> parse_int_string(UC const* p, UC const* pend, T& value, int base)
{
from_chars_result_t<UC> answer;
UC const* const first = p;
bool negative = (*p == UC('-'));
if (!std::is_signed<T>::value && negative) {
answer.ec = std::errc::invalid_argument;
answer.ptr = first;
return answer;
}
#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
if ((*p == UC('-')) || (*p == UC('+'))) {
#else
if (*p == UC('-')) {
#endif
++p;
}
UC const* const start_num = p;
while (*p == UC('0')) {
++p;
}
const bool has_leading_zeros = p > start_num;
UC const* const start_digits = p;
uint64_t i = 0;
if (base == 10) {
loop_parse_if_eight_digits(p, pend, i); // use SIMD if possible
}
while (p != pend) {
uint8_t digit = ch_to_digit(*p);
if (digit >= base) {
break;
}
i = uint64_t(base) * i + digit; // might overflow, check this later
p++;
}
size_t digit_count = size_t(p - start_digits);
if (digit_count == 0) {
if (has_leading_zeros) {
value = 0;
answer.ec = std::errc();
answer.ptr = p;
}
else {
answer.ec = std::errc::invalid_argument;
answer.ptr = first;
}
return answer;
}
answer.ptr = p;
// check u64 overflow
size_t max_digits = max_digits_u64(base);
if (digit_count > max_digits) {
answer.ec = std::errc::result_out_of_range;
return answer;
}
// this check can be eliminated for all other types, but they will all require a max_digits(base) equivalent
if (digit_count == max_digits && i < min_safe_u64(base)) {
answer.ec = std::errc::result_out_of_range;
return answer;
}
// check other types overflow
if (!std::is_same<T, uint64_t>::value) {
if (i > uint64_t(std::numeric_limits<T>::max()) + uint64_t(negative)) {
answer.ec = std::errc::result_out_of_range;
return answer;
}
}
if (negative) {
#ifdef FASTFLOAT_VISUAL_STUDIO
#pragma warning(push)
#pragma warning(disable: 4146)
#endif
// this weird workaround is required because:
// - converting unsigned to signed when its value is greater than signed max is UB pre-C++23.
// - reinterpret_casting (~i + 1) would work, but it is not constexpr
// this is always optimized into a neg instruction.
value = T(-std::numeric_limits<T>::max() - T(i - std::numeric_limits<T>::max()));
#ifdef FASTFLOAT_VISUAL_STUDIO
#pragma warning(pop)
#endif
}
else { value = T(i); }
answer.ec = std::errc();
return answer;
}
} // namespace fast_float } // namespace fast_float
#endif #endif

View File

@ -24,7 +24,7 @@ namespace fast_float {
* to determine whether we allow the fixed point and scientific notation respectively. * to determine whether we allow the fixed point and scientific notation respectively.
* The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`. * The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`.
*/ */
template<typename T, typename UC = char> template<typename T, typename UC = char, typename = FASTFLOAT_ENABLE_IF(is_supported_float_type<T>())>
FASTFLOAT_CONSTEXPR20 FASTFLOAT_CONSTEXPR20
from_chars_result_t<UC> from_chars(UC const * first, UC const * last, from_chars_result_t<UC> from_chars(UC const * first, UC const * last,
T &value, chars_format fmt = chars_format::general) noexcept; T &value, chars_format fmt = chars_format::general) noexcept;
@ -36,6 +36,12 @@ template<typename T, typename UC = char>
FASTFLOAT_CONSTEXPR20 FASTFLOAT_CONSTEXPR20
from_chars_result_t<UC> from_chars_advanced(UC const * first, UC const * last, from_chars_result_t<UC> from_chars_advanced(UC const * first, UC const * last,
T &value, parse_options_t<UC> options) noexcept; T &value, parse_options_t<UC> options) noexcept;
/**
* from_chars for integer types.
*/
template <typename T, typename UC = char, typename = FASTFLOAT_ENABLE_IF(!is_supported_float_type<T>())>
FASTFLOAT_CONSTEXPR20
from_chars_result_t<UC> from_chars(UC const * first, UC const * last, T& value, int base = 10) noexcept;
} // namespace fast_float } // namespace fast_float
#include "parse_number.h" #include "parse_number.h"

View File

@ -134,7 +134,7 @@ using parse_options = parse_options_t<char>;
#define FASTFLOAT_NEON 1 #define FASTFLOAT_NEON 1
#endif #endif
#if defined(FASTFLOAT_SSE2) || defined(FASTFLOAT_ARM64) #if defined(FASTFLOAT_SSE2) || defined(FASTFLOAT_NEON)
#define FASTFLOAT_HAS_SIMD 1 #define FASTFLOAT_HAS_SIMD 1
#endif #endif
@ -173,7 +173,7 @@ using parse_options = parse_options_t<char>;
// rust style `try!()` macro, or `?` operator // rust style `try!()` macro, or `?` operator
#define FASTFLOAT_TRY(x) { if (!(x)) return false; } #define FASTFLOAT_TRY(x) { if (!(x)) return false; }
#define FASTFLOAT_ENABLE_IF(...) typename std::enable_if<(__VA_ARGS__), int>::type = 0 #define FASTFLOAT_ENABLE_IF(...) typename std::enable_if<(__VA_ARGS__), int>::type
namespace fast_float { namespace fast_float {
@ -186,6 +186,20 @@ fastfloat_really_inline constexpr bool cpp20_and_in_constexpr() {
#endif #endif
} }
template <typename T>
fastfloat_really_inline constexpr bool is_supported_float_type() {
return std::is_same<T, float>::value || std::is_same<T, double>::value;
}
template <typename UC>
fastfloat_really_inline constexpr bool is_supported_char_type() {
return
std::is_same<UC, char>::value ||
std::is_same<UC, wchar_t>::value ||
std::is_same<UC, char16_t>::value ||
std::is_same<UC, char32_t>::value;
}
// Compares two ASCII strings in a case insensitive manner. // Compares two ASCII strings in a case insensitive manner.
template <typename UC> template <typename UC>
inline FASTFLOAT_CONSTEXPR14 bool inline FASTFLOAT_CONSTEXPR14 bool
@ -270,10 +284,10 @@ uint64_t umul128_generic(uint64_t ab, uint64_t cd, uint64_t *hi) {
uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd); uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd);
uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd); uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd);
uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32)); uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32));
uint64_t adbc_carry = !!(adbc < ad); uint64_t adbc_carry = (uint64_t)(adbc < ad);
uint64_t lo = bd + (adbc << 32); uint64_t lo = bd + (adbc << 32);
*hi = emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) + *hi = emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) +
(adbc_carry << 32) + !!(lo < bd); (adbc_carry << 32) + (uint64_t)(lo < bd);
return lo; return lo;
} }
@ -674,6 +688,69 @@ constexpr char32_t const * str_const_inf<char32_t>()
{ {
return U"infinity"; return U"infinity";
} }
template <typename = void>
struct int_luts {
static constexpr uint8_t chdigit[] = {
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255,
255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255,
255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
};
static constexpr size_t maxdigits_u64[] = {
64, 41, 32, 28, 25, 23, 22, 21,
20, 19, 18, 18, 17, 17, 16, 16,
16, 16, 15, 15, 15, 15, 14, 14,
14, 14, 14, 14, 14, 13, 13, 13,
13, 13, 13
};
static constexpr uint64_t min_safe_u64[] = {
9223372036854775808ull, 12157665459056928801ull, 4611686018427387904, 7450580596923828125, 4738381338321616896,
3909821048582988049, 9223372036854775808ull, 12157665459056928801ull, 10000000000000000000ull, 5559917313492231481,
2218611106740436992, 8650415919381337933, 2177953337809371136, 6568408355712890625, 1152921504606846976,
2862423051509815793, 6746640616477458432, 15181127029874798299ull, 1638400000000000000, 3243919932521508681,
6221821273427820544, 11592836324538749809ull, 876488338465357824, 1490116119384765625, 2481152873203736576,
4052555153018976267, 6502111422497947648, 10260628712958602189ull, 15943230000000000000ull, 787662783788549761,
1152921504606846976, 1667889514952984961, 2386420683693101056, 3379220508056640625, 4738381338321616896
};
};
template <typename T>
constexpr uint8_t int_luts<T>::chdigit[];
template <typename T>
constexpr size_t int_luts<T>::maxdigits_u64[];
template <typename T>
constexpr uint64_t int_luts<T>::min_safe_u64[];
template <typename UC>
fastfloat_really_inline
constexpr uint8_t ch_to_digit(UC c) { return int_luts<>::chdigit[static_cast<unsigned char>(c)]; }
fastfloat_really_inline
constexpr size_t max_digits_u64(int base) { return int_luts<>::maxdigits_u64[base - 2]; }
// If a u64 is exactly max_digits_u64() in length, this is
// the value below which it has definitely overflowed.
fastfloat_really_inline
constexpr uint64_t min_safe_u64(int base) { return int_luts<>::min_safe_u64[base - 2]; }
} // namespace fast_float } // namespace fast_float
#endif #endif

View File

@ -186,7 +186,6 @@ struct from_chars_caller<std::float64_t>
template<typename T, typename UC> template<typename T, typename UC>
FASTFLOAT_CONSTEXPR20
from_chars_result_t<UC> from_chars(UC const * first, UC const * last, from_chars_result_t<UC> from_chars(UC const * first, UC const * last,
T &value, chars_format fmt /*= chars_format::general*/) noexcept { T &value, chars_format fmt /*= chars_format::general*/) noexcept {
return from_chars_caller<T>::call(first, last, value, parse_options_t<UC>(fmt)); return from_chars_caller<T>::call(first, last, value, parse_options_t<UC>(fmt));
@ -197,11 +196,8 @@ FASTFLOAT_CONSTEXPR20
from_chars_result_t<UC> from_chars_advanced(UC const * first, UC const * last, from_chars_result_t<UC> from_chars_advanced(UC const * first, UC const * last,
T &value, parse_options_t<UC> options) noexcept { T &value, parse_options_t<UC> options) noexcept {
static_assert (std::is_same<T, double>::value || std::is_same<T, float>::value, "only float and double are supported"); static_assert (is_supported_float_type<T>(), "only float and double are supported");
static_assert (std::is_same<UC, char>::value || static_assert (is_supported_char_type<UC>(), "only char, wchar_t, char16_t and char32_t are supported");
std::is_same<UC, wchar_t>::value ||
std::is_same<UC, char16_t>::value ||
std::is_same<UC, char32_t>::value , "only char, wchar_t, char16_t and char32_t are supported");
from_chars_result_t<UC> answer; from_chars_result_t<UC> answer;
#ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default #ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default
@ -284,6 +280,27 @@ from_chars_result_t<UC> from_chars_advanced(UC const * first, UC const * last,
return answer; return answer;
} }
template <typename T, typename UC, typename>
FASTFLOAT_CONSTEXPR20
from_chars_result_t<UC> from_chars(UC const* first, UC const* last, T& value, int base) noexcept
{
static_assert (is_supported_char_type<UC>(), "only char, wchar_t, char16_t and char32_t are supported");
from_chars_result_t<UC> answer;
#ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default
while ((first != last) && fast_float::is_space(uint8_t(*first))) {
first++;
}
#endif
if (first == last || base < 2 || base > 36) {
answer.ec = std::errc::invalid_argument;
answer.ptr = first;
return answer;
}
return parse_int_string(first, last, value, base);
}
} // namespace fast_float } // namespace fast_float
#endif #endif

View File

@ -5,16 +5,18 @@ cmake_minimum_required(VERSION 3.11 FATAL_ERROR)
include(FetchContent) include(FetchContent)
option(SYSTEM_DOCTEST "Use system copy of doctest" OFF) option(SYSTEM_DOCTEST "Use system copy of doctest" OFF)
option(FASTFLOAT_SUPPLEMENTAL_TESTS "Run supplemental tests" ON)
if (NOT SYSTEM_DOCTEST) if (NOT SYSTEM_DOCTEST)
FetchContent_Declare(doctest FetchContent_Declare(doctest
GIT_REPOSITORY https://github.com/onqtam/doctest.git GIT_REPOSITORY https://github.com/onqtam/doctest.git
GIT_TAG v2.4.10) GIT_TAG v2.4.10)
endif() endif()
FetchContent_Declare(supplemental_test_files if (FASTFLOAT_SUPPLEMENTAL_TESTS)
GIT_REPOSITORY https://github.com/fastfloat/supplemental_test_files.git FetchContent_Declare(supplemental_test_files
GIT_TAG origin/main) GIT_REPOSITORY https://github.com/fastfloat/supplemental_test_files.git
GIT_TAG origin/main)
endif()
# FetchContent_MakeAvailable() was only introduced in 3.14 # FetchContent_MakeAvailable() was only introduced in 3.14
@ -27,15 +29,19 @@ if (NOT SYSTEM_DOCTEST)
add_subdirectory(${doctest_SOURCE_DIR} ${doctest_BINARY_DIR}) add_subdirectory(${doctest_SOURCE_DIR} ${doctest_BINARY_DIR})
endif() endif()
endif() endif()
FetchContent_GetProperties(supplemental_test_files)
if(NOT supplemental_test_files_POPULATED)
message(STATUS "Tests enabled. Retrieving test files.")
FetchContent_Populate(supplemental_test_files)
message(STATUS "Test files retrieved.")
add_subdirectory(${supplemental_test_files_SOURCE_DIR} ${supplemental_test_files_BINARY_DIR})
endif()
add_library(supplemental-data INTERFACE) add_library(supplemental-data INTERFACE)
target_compile_definitions(supplemental-data INTERFACE SUPPLEMENTAL_TEST_DATA_DIR="${supplemental_test_files_BINARY_DIR}/data") if (FASTFLOAT_SUPPLEMENTAL_TESTS)
FetchContent_GetProperties(supplemental_test_files)
if(NOT supplemental_test_files_POPULATED)
message(STATUS "Supplemental tests enabled. Retrieving test files.")
FetchContent_Populate(supplemental_test_files)
message(STATUS "Supplemental test files retrieved.")
add_subdirectory(${supplemental_test_files_SOURCE_DIR} ${supplemental_test_files_BINARY_DIR})
endif()
target_compile_definitions(supplemental-data INTERFACE SUPPLEMENTAL_TEST_DATA_DIR="${supplemental_test_files_BINARY_DIR}/data")
endif()
function(fast_float_add_cpp_test TEST_NAME) function(fast_float_add_cpp_test TEST_NAME)
add_executable(${TEST_NAME} ${TEST_NAME}.cpp) add_executable(${TEST_NAME} ${TEST_NAME}.cpp)
if(CMAKE_CROSSCOMPILING) if(CMAKE_CROSSCOMPILING)
@ -67,11 +73,14 @@ if (FASTFLOAT_CONSTEXPR_TESTS)
else() else()
target_compile_features(basictest PRIVATE cxx_std_17) target_compile_features(basictest PRIVATE cxx_std_17)
endif() endif()
if (FASTFLOAT_SUPPLEMENTAL_TESTS)
target_compile_definitions(basictest PRIVATE FASTFLOAT_SUPPLEMENTAL_TESTS)
endif()
fast_float_add_cpp_test(long_test) fast_float_add_cpp_test(long_test)
fast_float_add_cpp_test(powersoffive_hardround) fast_float_add_cpp_test(powersoffive_hardround)
fast_float_add_cpp_test(string_test) fast_float_add_cpp_test(string_test)
fast_float_add_cpp_test(fast_int)
fast_float_add_cpp_test(json_fmt) fast_float_add_cpp_test(json_fmt)
fast_float_add_cpp_test(fortran) fast_float_add_cpp_test(fortran)

View File

@ -246,6 +246,7 @@ TEST_CASE("parse_negative_zero") {
CHECK(float64_parsed == 0x8000'0000'0000'0000); CHECK(float64_parsed == 0x8000'0000'0000'0000);
} }
#if FASTFLOAT_SUPPLEMENTAL_TESTS
// C++ 17 because it is otherwise annoying to browse all files in a directory. // C++ 17 because it is otherwise annoying to browse all files in a directory.
// We also only run these tests on little endian systems. // We also only run these tests on little endian systems.
#if (FASTFLOAT_CPLUSPLUS >= 201703L) && (FASTFLOAT_IS_BIG_ENDIAN == 0) && !defined(FASTFLOAT_ODDPLATFORM) #if (FASTFLOAT_CPLUSPLUS >= 201703L) && (FASTFLOAT_IS_BIG_ENDIAN == 0) && !defined(FASTFLOAT_ODDPLATFORM)
@ -336,6 +337,7 @@ TEST_CASE("supplemental") {
} }
} }
#endif #endif
#endif
TEST_CASE("leading_zeroes") { TEST_CASE("leading_zeroes") {
constexpr const uint64_t bit = 1; constexpr const uint64_t bit = 1;
@ -825,6 +827,7 @@ TEST_CASE("64bit.inf") {
} }
TEST_CASE("64bit.general") { TEST_CASE("64bit.general") {
verify("0.95000000000000000000",0.95);
verify("22250738585072012e-324",0x1p-1022); /* limit between normal and subnormal*/ verify("22250738585072012e-324",0x1p-1022); /* limit between normal and subnormal*/
verify("-22250738585072012e-324",-0x1p-1022); /* limit between normal and subnormal*/ verify("-22250738585072012e-324",-0x1p-1022); /* limit between normal and subnormal*/
verify("-1e-999", -0.0, std::errc::result_out_of_range); verify("-1e-999", -0.0, std::errc::result_out_of_range);

752
tests/fast_int.cpp Normal file
View File

@ -0,0 +1,752 @@
#include <cstdlib>
#include <iostream>
#include <vector>
#include <iomanip>
#include <cstring>
#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
const std::vector<int> int_basic_test_expected { 0, 10, -40, 1001, 9 };
const std::vector<std::string> int_basic_test { "0", "10 ", "-40", "1001 with text", "9.999" };
for (std::size_t i = 0; i < int_basic_test.size(); ++i)
{
const auto& 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: " << std::quoted(f) << " because of invalid arguement" << std::endl;
}
else if (answer.ec == std::errc::result_out_of_range) {
std::cerr << "could not convert to int for input: " << std::quoted(f) << " because it's out of range" << std::endl;
}
else {
std::cerr << "could not convert to int for input: " << std::quoted(f) << " because of an unknown error" << std::endl;
}
return EXIT_FAILURE;
}
else if (result != int_basic_test_expected[i]) {
std::cerr << "result " << std::quoted(f) << " did not match with expected int: " << int_basic_test_expected[i] << std::endl;
return EXIT_FAILURE;
}
}
// unsigned basic test
const std::vector<unsigned> unsigned_basic_test_expected { 0, 10, 1001, 9 };
const std::vector<std::string> unsigned_basic_test { "0", "10 ", "1001 with text", "9.999" };
for (std::size_t i = 0; i < unsigned_basic_test.size(); ++i)
{
const auto& 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: " << std::quoted(f) << std::endl;
return EXIT_FAILURE;
}
else if (result != unsigned_basic_test_expected[i]) {
std::cerr << "result " << std::quoted(f) << " did not match with expected unsigned: " << unsigned_basic_test_expected[i] << std::endl;
return EXIT_FAILURE;
}
}
// int invalid error test
const std::vector<std::string> int_invalid_argument_test{ "text", "text with 1002", "+50", " 50" };
for (std::size_t i = 0; i < int_invalid_argument_test.size(); ++i)
{
const auto& 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: " << std::quoted(f) << std::endl;
return EXIT_FAILURE;
}
}
// unsigned invalid error test
const std::vector<std::string> unsigned_invalid_argument_test{ "text", "text with 1002", "+50", " 50", "-50" };
for (std::size_t i = 0; i < unsigned_invalid_argument_test.size(); ++i)
{
const auto& 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: " << std::quoted(f) << std::endl;
return EXIT_FAILURE;
}
}
// int out of range error test #1 (8 bit)
const std::vector<std::string> int_out_of_range_test_1{ "2000000000000000000000", "128", "-129"};
for (std::size_t i = 0; i < int_out_of_range_test_1.size(); ++i)
{
const auto& f = int_out_of_range_test_1[i];
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': " << std::quoted(f) << std::endl;
return EXIT_FAILURE;
}
}
// int out of range error test #2 (16 bit)
const std::vector<std::string> int_out_of_range_test_2{ "2000000000000000000000", "32768", "-32769"};
for (std::size_t i = 0; i < int_out_of_range_test_2.size(); ++i)
{
const auto& f = int_out_of_range_test_2[i];
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': " << std::quoted(f) << std::endl;
return EXIT_FAILURE;
}
}
// int out of range error test #3 (32 bit)
const std::vector<std::string> int_out_of_range_test_3{ "2000000000000000000000", "2147483648", "-2147483649"};
for (std::size_t i = 0; i < int_out_of_range_test_3.size(); ++i)
{
const auto& f = int_out_of_range_test_3[i];
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': " << std::quoted(f) << std::endl;
return EXIT_FAILURE;
}
}
// int out of range error test #4 (64 bit)
const std::vector<std::string> int_out_of_range_test_4{ "2000000000000000000000", "9223372036854775808", "-9223372036854775809"};
for (std::size_t i = 0; i < int_out_of_range_test_4.size(); ++i)
{
const auto& f = int_out_of_range_test_4[i];
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': " << std::quoted(f) << std::endl;
return EXIT_FAILURE;
}
}
// unsigned out of range error test #1 (8 bit)
const std::vector<std::string> unsigned_out_of_range_test_1{ "2000000000000000000000", "256" };
for (std::size_t i = 0; i < unsigned_out_of_range_test_1.size(); ++i)
{
const auto& f = unsigned_out_of_range_test_1[i];
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': " << std::quoted(f) << std::endl;
return EXIT_FAILURE;
}
}
// unsigned out of range error test #2 (16 bit)
const std::vector<std::string> unsigned_out_of_range_test_2{ "2000000000000000000000", "65536" };
for (std::size_t i = 0; i < unsigned_out_of_range_test_2.size(); ++i)
{
const auto& f = unsigned_out_of_range_test_2[i];
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': " << std::quoted(f) << std::endl;
return EXIT_FAILURE;
}
}
// unsigned out of range error test #3 (32 bit)
const std::vector<std::string> unsigned_out_of_range_test_3{ "2000000000000000000000", "4294967296" };
for (std::size_t i = 0; i < unsigned_out_of_range_test_3.size(); ++i)
{
const auto& f = unsigned_out_of_range_test_3[i];
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': " << std::quoted(f) << std::endl;
return EXIT_FAILURE;
}
}
// unsigned out of range error test #4 (64 bit)
const std::vector<std::string> unsigned_out_of_range_test_4{ "2000000000000000000000", "18446744073709551616" };
for (std::size_t i = 0; i < unsigned_out_of_range_test_4.size(); ++i)
{
const auto& f = unsigned_out_of_range_test_4[i];
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': " << std::quoted(f) << std::endl;
return EXIT_FAILURE;
}
}
// int pointer test #1 (only numbers)
const std::vector<std::string> int_pointer_test_1 { "0", "010", "-40" };
for (std::size_t i = 0; i < int_pointer_test_1.size(); ++i)
{
const auto& 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: " << std::quoted(f) << std::endl;
return EXIT_FAILURE;
}
else if (strcmp(answer.ptr, "") != 0) {
std::cerr << "ptr of result " << std::quoted(f) << " did not match with expected ptr: " << std::quoted("") << std::endl;
return EXIT_FAILURE;
}
}
// int pointer test #2 (string behind numbers)
const std::string int_pointer_test_2 = "1001 with text";
const auto& 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 " << std::quoted(f2) << " did not match with expected ptr: " << std::quoted(" with text") << std::endl;
return EXIT_FAILURE;
}
// int pointer test #3 (string with newline behind numbers)
const std::string int_pointer_test_3 = "1001 with text\n";
const auto& 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 " << std::quoted(f3) << " did not match with expected ptr: " << std::quoted(" with text") << std::endl;
return EXIT_FAILURE;
}
// int pointer test #4 (float)
const std::string int_pointer_test_4 = "9.999";
const auto& 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 " << std::quoted(f4) << " did not match with expected ptr: " << std::quoted(".999") << std::endl;
return EXIT_FAILURE;
}
// int pointer test #5 (invalid int)
const std::string int_pointer_test_5 = "+50";
const auto& 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 " << std::quoted(f5) << " did not match with expected ptr: " << std::quoted("+50") << std::endl;
return EXIT_FAILURE;
}
// unsigned pointer test #2 (string behind numbers)
const std::string unsigned_pointer_test_1 = "1001 with text";
const auto& 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 " << std::quoted(f6) << " did not match with expected ptr: " << std::quoted(" with text") << std::endl;
return EXIT_FAILURE;
}
// unsigned pointer test #2 (invalid unsigned)
const std::string unsigned_pointer_test_2 = "-50";
const auto& 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 " << std::quoted(f7) << " did not match with expected ptr: " << std::quoted("-50") << std::endl;
return EXIT_FAILURE;
}
// int base 2 test
const std::vector<int> int_base_2_test_expected { 0, 1, 4, 2, -1 };
const std::vector<std::string> int_base_2_test { "0", "1", "100", "010", "-1" };
for (std::size_t i = 0; i < int_base_2_test.size(); ++i)
{
const auto& 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: " << std::quoted(f) << std::endl;
return EXIT_FAILURE;
}
else if (result != int_base_2_test_expected[i]) {
std::cerr << "result " << std::quoted(f) << " did not match with expected int: " << int_base_2_test_expected[i] << std::endl;
return EXIT_FAILURE;
}
}
// unsigned base 2 test
const std::vector<unsigned> unsigned_base_2_test_expected { 0, 1, 4, 2 };
const std::vector<std::string> unsigned_base_2_test { "0", "1", "100", "010" };
for (std::size_t i = 0; i < unsigned_base_2_test.size(); ++i)
{
const auto& 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: " << std::quoted(f) << std::endl;
return EXIT_FAILURE;
}
else if (result != unsigned_base_2_test_expected[i]) {
std::cerr << "result " << std::quoted(f) << " did not match with expected unsigned: " << unsigned_base_2_test_expected[i] << std::endl;
return EXIT_FAILURE;
}
}
// int invalid error base 2 test
const std::vector<std::string> int_invalid_argument_base_2_test{ "2", "A", "-2" };
for (std::size_t i = 0; i < int_invalid_argument_base_2_test.size(); ++i)
{
const auto& f = int_invalid_argument_base_2_test[i];
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: " << std::quoted(f) << std::endl;
return EXIT_FAILURE;
}
}
// unsigned invalid error base 2 test
const std::vector<std::string> 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)
{
const auto& 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: " << std::quoted(f) << std::endl;
return EXIT_FAILURE;
}
}
// octal test
const std::vector<int> base_octal_test_expected {0, 1, 7, 8, 9};
const std::vector<std::string> base_octal_test { "0", "1", "07", "010", "0011" };
for (std::size_t i = 0; i < base_octal_test.size(); ++i)
{
const auto& 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: " << std::quoted(f) << std::endl;
return EXIT_FAILURE;
}
else if (result != base_octal_test_expected[i]) {
std::cerr << "result " << std::quoted(f) << " did not match with expected int: " << base_octal_test_expected[i] << std::endl;
return EXIT_FAILURE;
}
}
// hex test
const std::vector<int> base_hex_test_expected { 0, 1, 15, 31, 0, 16};
const std::vector<std::string> base_hex_test { "0", "1", "F", "01f", "0x11", "10X11" };
for (std::size_t i = 0; i < base_hex_test.size(); ++i)
{
const auto& 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: " << std::quoted(f) << std::endl;
return EXIT_FAILURE;
}
else if (result != base_hex_test_expected[i]) {
std::cerr << "result " << std::quoted(f) << " did not match with expected int: " << base_hex_test_expected[i] << std::endl;
return EXIT_FAILURE;
}
}
// invalid base test #1 (-1)
const std::vector<std::string> invalid_base_test_1 { "0", "1", "-1", "F", "10Z" };
for (std::size_t i = 0; i < invalid_base_test_1.size(); ++i)
{
const auto& 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: " << std::quoted(f) << std::endl;
return EXIT_FAILURE;
}
}
// invalid base test #2 (37)
const std::vector<std::string> invalid_base_test_2 { "0", "1", "F", "Z", "10Z" };
for (std::size_t i = 0; i < invalid_base_test_2.size(); ++i)
{
const auto& 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: " << std::quoted(f) << std::endl;
return EXIT_FAILURE;
}
}
// int out of range error base test (64 bit)
const std::vector<std::string> 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)
{
const auto& 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': " << std::quoted(f) << std::endl;
return EXIT_FAILURE;
}
}
// unsigned out of range error base test (64 bit)
const std::vector<std::string> 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)
{
const auto& 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': " << std::quoted(f) << std::endl;
return EXIT_FAILURE;
}
++base_unsigned;
}
// just within range base test (64 bit)
const std::vector<std::string> 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)
{
const auto& 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 " << std::quoted(f) << " to int failed (most likely out of range)" << std::endl;
return EXIT_FAILURE;
}
}
// unsigned within range base test (64 bit)
const std::vector<std::string> 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)
{
const auto& 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 " << std::quoted(f) << " to unsigned failed (most likely out of range)" << std::endl;
return EXIT_FAILURE;
}
++base_unsigned2;
}
// int leading zeros test
const std::vector<std::string> int_leading_zeros_test { "00000000000000000000000000000000000000000000000000000000000000000000001111110111",
"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)
{
const auto& 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: " << std::quoted(f) << std::endl;
return EXIT_FAILURE;
}
else if (result != 1015) {
std::cerr << "result " << std::quoted(f) << " did not match with expected int: " << 1015 << std::endl;
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}