mirror of
https://github.com/fastfloat/fast_float.git
synced 2025-12-06 08:46:49 +08:00
Merge branch 'main' into main
This commit is contained in:
commit
620376ad39
@ -1,6 +1,6 @@
|
||||
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)
|
||||
if(FASTFLOAT_TEST)
|
||||
enable_testing()
|
||||
|
||||
55
README.md
55
README.md
@ -4,17 +4,21 @@
|
||||
[](https://github.com/fastfloat/fast_float/actions/workflows/ubuntu22.yml)
|
||||
|
||||
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
|
||||
decimal values (e.g., `1.3e10`) into binary types. We provide exact rounding (including
|
||||
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
|
||||
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++
|
||||
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, ...);
|
||||
```
|
||||
|
||||
You can also parse integer types:
|
||||
|
||||
|
||||
|
||||
|
||||
The return type (`from_chars_result`) is defined as the struct:
|
||||
```C++
|
||||
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`).
|
||||
|
||||
|
||||
## 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)
|
||||
|
||||
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
|
||||
|
||||
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?
|
||||
@ -330,7 +371,11 @@ the command line help.
|
||||
|
||||
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
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#include "float_common.h"
|
||||
@ -115,7 +116,7 @@ FASTFLOAT_SIMD_RESTORE_WARNINGS
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1900
|
||||
template <typename UC>
|
||||
#else
|
||||
template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>())>
|
||||
template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>()) = 0>
|
||||
#endif
|
||||
// dummy for compile
|
||||
uint64_t simd_read8_to_u64(UC const*) {
|
||||
@ -223,7 +224,7 @@ FASTFLOAT_SIMD_RESTORE_WARNINGS
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1900
|
||||
template <typename UC>
|
||||
#else
|
||||
template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>())>
|
||||
template <typename UC, FASTFLOAT_ENABLE_IF(!has_simd_opt<UC>()) = 0>
|
||||
#endif
|
||||
// dummy for compile
|
||||
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
|
||||
void loop_parse_if_eight_digits(const UC*& p, const UC* const pend, uint64_t& i) {
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
#endif
|
||||
|
||||
@ -24,7 +24,7 @@ namespace fast_float {
|
||||
* 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`.
|
||||
*/
|
||||
template<typename T, typename UC = char>
|
||||
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, chars_format fmt = chars_format::general) noexcept;
|
||||
@ -36,6 +36,12 @@ template<typename T, typename UC = char>
|
||||
FASTFLOAT_CONSTEXPR20
|
||||
from_chars_result_t<UC> from_chars_advanced(UC const * first, UC const * last,
|
||||
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
|
||||
#include "parse_number.h"
|
||||
|
||||
@ -134,7 +134,7 @@ using parse_options = parse_options_t<char>;
|
||||
#define FASTFLOAT_NEON 1
|
||||
#endif
|
||||
|
||||
#if defined(FASTFLOAT_SSE2) || defined(FASTFLOAT_ARM64)
|
||||
#if defined(FASTFLOAT_SSE2) || defined(FASTFLOAT_NEON)
|
||||
#define FASTFLOAT_HAS_SIMD 1
|
||||
#endif
|
||||
|
||||
@ -173,7 +173,7 @@ using parse_options = parse_options_t<char>;
|
||||
// rust style `try!()` macro, or `?` operator
|
||||
#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 {
|
||||
@ -186,6 +186,20 @@ fastfloat_really_inline constexpr bool cpp20_and_in_constexpr() {
|
||||
#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.
|
||||
template <typename UC>
|
||||
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 bd = emulu((uint32_t)ab, (uint32_t)cd);
|
||||
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);
|
||||
*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;
|
||||
}
|
||||
|
||||
@ -674,6 +688,69 @@ constexpr char32_t const * str_const_inf<char32_t>()
|
||||
{
|
||||
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
|
||||
|
||||
#endif
|
||||
|
||||
@ -186,7 +186,6 @@ struct from_chars_caller<std::float64_t>
|
||||
|
||||
|
||||
template<typename T, typename UC>
|
||||
FASTFLOAT_CONSTEXPR20
|
||||
from_chars_result_t<UC> from_chars(UC const * first, UC const * last,
|
||||
T &value, chars_format fmt /*= chars_format::general*/) noexcept {
|
||||
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,
|
||||
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 (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 , "only char, wchar_t, char16_t and char32_t are supported");
|
||||
static_assert (is_supported_float_type<T>(), "only float and double are supported");
|
||||
static_assert (is_supported_char_type<UC>(), "only char, wchar_t, char16_t and char32_t are supported");
|
||||
|
||||
from_chars_result_t<UC> answer;
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
||||
#endif
|
||||
|
||||
@ -5,16 +5,18 @@ cmake_minimum_required(VERSION 3.11 FATAL_ERROR)
|
||||
include(FetchContent)
|
||||
|
||||
option(SYSTEM_DOCTEST "Use system copy of doctest" OFF)
|
||||
option(FASTFLOAT_SUPPLEMENTAL_TESTS "Run supplemental tests" ON)
|
||||
|
||||
if (NOT SYSTEM_DOCTEST)
|
||||
FetchContent_Declare(doctest
|
||||
GIT_REPOSITORY https://github.com/onqtam/doctest.git
|
||||
GIT_TAG v2.4.10)
|
||||
endif()
|
||||
FetchContent_Declare(supplemental_test_files
|
||||
GIT_REPOSITORY https://github.com/fastfloat/supplemental_test_files.git
|
||||
GIT_TAG origin/main)
|
||||
|
||||
if (FASTFLOAT_SUPPLEMENTAL_TESTS)
|
||||
FetchContent_Declare(supplemental_test_files
|
||||
GIT_REPOSITORY https://github.com/fastfloat/supplemental_test_files.git
|
||||
GIT_TAG origin/main)
|
||||
endif()
|
||||
|
||||
|
||||
# FetchContent_MakeAvailable() was only introduced in 3.14
|
||||
@ -27,15 +29,19 @@ if (NOT SYSTEM_DOCTEST)
|
||||
add_subdirectory(${doctest_SOURCE_DIR} ${doctest_BINARY_DIR})
|
||||
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)
|
||||
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)
|
||||
add_executable(${TEST_NAME} ${TEST_NAME}.cpp)
|
||||
if(CMAKE_CROSSCOMPILING)
|
||||
@ -67,11 +73,14 @@ if (FASTFLOAT_CONSTEXPR_TESTS)
|
||||
else()
|
||||
target_compile_features(basictest PRIVATE cxx_std_17)
|
||||
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(powersoffive_hardround)
|
||||
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(fortran)
|
||||
|
||||
|
||||
@ -246,6 +246,7 @@ TEST_CASE("parse_negative_zero") {
|
||||
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.
|
||||
// We also only run these tests on little endian systems.
|
||||
#if (FASTFLOAT_CPLUSPLUS >= 201703L) && (FASTFLOAT_IS_BIG_ENDIAN == 0) && !defined(FASTFLOAT_ODDPLATFORM)
|
||||
@ -336,6 +337,7 @@ TEST_CASE("supplemental") {
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
TEST_CASE("leading_zeroes") {
|
||||
constexpr const uint64_t bit = 1;
|
||||
@ -825,6 +827,7 @@ TEST_CASE("64bit.inf") {
|
||||
}
|
||||
|
||||
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("-1e-999", -0.0, std::errc::result_out_of_range);
|
||||
|
||||
752
tests/fast_int.cpp
Normal file
752
tests/fast_int.cpp
Normal 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;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user