mirror of
https://github.com/fastfloat/fast_float.git
synced 2026-02-16 23:29:54 +08:00
Merge branch 'main' of https://github.com/fastfloat/fast_float
This commit is contained in:
commit
b28862bbaa
4
.github/workflows/s390x.yml
vendored
4
.github/workflows/s390x.yml
vendored
@ -22,9 +22,9 @@ jobs:
|
|||||||
distro: ubuntu_latest
|
distro: ubuntu_latest
|
||||||
install: |
|
install: |
|
||||||
apt-get update -q -y
|
apt-get update -q -y
|
||||||
apt-get install -y cmake make g++
|
apt-get install -y cmake make g++ git
|
||||||
run: |
|
run: |
|
||||||
cmake -DCMAKE_BUILD_TYPE=Release -B build
|
cmake -DCMAKE_BUILD_TYPE=Release -B build -DFASTFLOAT_TEST=ON
|
||||||
cmake --build build -j=2
|
cmake --build build -j=2
|
||||||
ctest --output-on-failure --test-dir build
|
ctest --output-on-failure --test-dir build
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.14)
|
cmake_minimum_required(VERSION 3.14)
|
||||||
|
|
||||||
|
|
||||||
project(fast_float VERSION 8.2.1 LANGUAGES CXX)
|
project(fast_float VERSION 8.2.2 LANGUAGES CXX)
|
||||||
set(FASTFLOAT_CXX_STANDARD 11 CACHE STRING "the C++ standard to use for fastfloat")
|
set(FASTFLOAT_CXX_STANDARD 11 CACHE STRING "the C++ standard to use for fastfloat")
|
||||||
set(CMAKE_CXX_STANDARD ${FASTFLOAT_CXX_STANDARD})
|
set(CMAKE_CXX_STANDARD ${FASTFLOAT_CXX_STANDARD})
|
||||||
option(FASTFLOAT_TEST "Enable tests" OFF)
|
option(FASTFLOAT_TEST "Enable tests" OFF)
|
||||||
|
|||||||
@ -569,7 +569,7 @@ sufficiently recent version of CMake (3.11 or better at least):
|
|||||||
FetchContent_Declare(
|
FetchContent_Declare(
|
||||||
fast_float
|
fast_float
|
||||||
GIT_REPOSITORY https://github.com/fastfloat/fast_float.git
|
GIT_REPOSITORY https://github.com/fastfloat/fast_float.git
|
||||||
GIT_TAG tags/v8.2.1
|
GIT_TAG tags/v8.2.2
|
||||||
GIT_SHALLOW TRUE)
|
GIT_SHALLOW TRUE)
|
||||||
|
|
||||||
FetchContent_MakeAvailable(fast_float)
|
FetchContent_MakeAvailable(fast_float)
|
||||||
@ -585,7 +585,7 @@ You may also use [CPM](https://github.com/cpm-cmake/CPM.cmake), like so:
|
|||||||
CPMAddPackage(
|
CPMAddPackage(
|
||||||
NAME fast_float
|
NAME fast_float
|
||||||
GITHUB_REPOSITORY "fastfloat/fast_float"
|
GITHUB_REPOSITORY "fastfloat/fast_float"
|
||||||
GIT_TAG v8.2.1)
|
GIT_TAG v8.2.2)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Using as single header
|
## Using as single header
|
||||||
@ -597,7 +597,7 @@ if desired as described in 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/v8.2.1/fast_float.h>
|
<https://github.com/fastfloat/fast_float/releases/download/v8.2.2/fast_float.h>
|
||||||
|
|
||||||
## Benchmarking
|
## Benchmarking
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,9 @@ FetchContent_MakeAvailable(counters)
|
|||||||
add_executable(realbenchmark benchmark.cpp)
|
add_executable(realbenchmark benchmark.cpp)
|
||||||
target_link_libraries(realbenchmark PRIVATE counters::counters)
|
target_link_libraries(realbenchmark PRIVATE counters::counters)
|
||||||
add_executable(bench_ip bench_ip.cpp)
|
add_executable(bench_ip bench_ip.cpp)
|
||||||
|
add_executable(bench_uint16 bench_uint16.cpp)
|
||||||
target_link_libraries(bench_ip PRIVATE counters::counters)
|
target_link_libraries(bench_ip PRIVATE counters::counters)
|
||||||
|
target_link_libraries(bench_uint16 PRIVATE counters::counters)
|
||||||
|
|
||||||
set_property(
|
set_property(
|
||||||
TARGET realbenchmark
|
TARGET realbenchmark
|
||||||
@ -19,8 +21,12 @@ set_property(
|
|||||||
set_property(
|
set_property(
|
||||||
TARGET bench_ip
|
TARGET bench_ip
|
||||||
PROPERTY CXX_STANDARD 17)
|
PROPERTY CXX_STANDARD 17)
|
||||||
|
set_property(
|
||||||
|
TARGET bench_uint16
|
||||||
|
PROPERTY CXX_STANDARD 17)
|
||||||
target_link_libraries(realbenchmark PUBLIC fast_float)
|
target_link_libraries(realbenchmark PUBLIC fast_float)
|
||||||
target_link_libraries(bench_ip PUBLIC fast_float)
|
target_link_libraries(bench_ip PUBLIC fast_float)
|
||||||
|
target_link_libraries(bench_uint16 PUBLIC fast_float)
|
||||||
|
|
||||||
include(ExternalProject)
|
include(ExternalProject)
|
||||||
|
|
||||||
|
|||||||
139
benchmarks/bench_uint16.cpp
Normal file
139
benchmarks/bench_uint16.cpp
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
#include "counters/bench.h"
|
||||||
|
#include "fast_float/fast_float.h"
|
||||||
|
#include <charconv>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <random>
|
||||||
|
#include <atomic>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
void pretty_print(size_t volume, size_t bytes, std::string name,
|
||||||
|
counters::event_aggregate agg) {
|
||||||
|
if (agg.inner_count > 1) {
|
||||||
|
printf("# (inner count: %d)\n", agg.inner_count);
|
||||||
|
}
|
||||||
|
printf("%-40s : ", name.c_str());
|
||||||
|
printf(" %5.2f GB/s ", bytes / agg.fastest_elapsed_ns());
|
||||||
|
printf(" %5.1f Mip/s ", volume * 1000.0 / agg.fastest_elapsed_ns());
|
||||||
|
printf(" %5.2f ns/ip ", agg.fastest_elapsed_ns() / volume);
|
||||||
|
if (counters::event_collector().has_events()) {
|
||||||
|
printf(" %5.2f GHz ", agg.fastest_cycles() / agg.fastest_elapsed_ns());
|
||||||
|
printf(" %5.2f c/ip ", agg.fastest_cycles() / volume);
|
||||||
|
printf(" %5.2f i/ip ", agg.fastest_instructions() / volume);
|
||||||
|
printf(" %5.2f c/b ", agg.fastest_cycles() / bytes);
|
||||||
|
printf(" %5.2f i/b ", agg.fastest_instructions() / bytes);
|
||||||
|
printf(" %5.2f i/c ", agg.fastest_instructions() / agg.fastest_cycles());
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class parse_method { standard, fast_float };
|
||||||
|
|
||||||
|
void validate(const std::string &buffer, const std::vector<uint16_t> &expected,
|
||||||
|
char delimiter) {
|
||||||
|
const char *p = buffer.data();
|
||||||
|
const char *pend = p + buffer.size();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < expected.size(); i++) {
|
||||||
|
uint16_t val;
|
||||||
|
auto r = fast_float::from_chars(p, pend, val);
|
||||||
|
if (r.ec != std::errc() || val != expected[i]) {
|
||||||
|
printf("Validation failed at index %zu: expected %u, got %u\n", i,
|
||||||
|
expected[i], val);
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
p = r.ptr;
|
||||||
|
if (i + 1 < expected.size()) {
|
||||||
|
if (p >= pend || *p != delimiter) {
|
||||||
|
printf("Validation failed at index %zu: delimiter mismatch\n", i);
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p != pend) {
|
||||||
|
printf("Validation failed: trailing bytes remain\n");
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
printf("Validation passed!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
constexpr size_t N = 500000;
|
||||||
|
constexpr char delimiter = ',';
|
||||||
|
std::mt19937 rng(1234);
|
||||||
|
std::uniform_int_distribution<int> dist(0, 65535);
|
||||||
|
|
||||||
|
std::vector<uint16_t> expected;
|
||||||
|
expected.reserve(N);
|
||||||
|
|
||||||
|
std::string buffer;
|
||||||
|
buffer.reserve(N * 6); // up to 5 digits + delimiter
|
||||||
|
|
||||||
|
for (size_t i = 0; i < N; ++i) {
|
||||||
|
uint16_t val = (uint16_t)dist(rng);
|
||||||
|
expected.push_back(val);
|
||||||
|
std::string s = std::to_string(val);
|
||||||
|
buffer.append(s);
|
||||||
|
if (i + 1 < N) {
|
||||||
|
buffer.push_back(delimiter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t total_bytes = buffer.size();
|
||||||
|
|
||||||
|
validate(buffer, expected, delimiter);
|
||||||
|
|
||||||
|
volatile uint64_t sink = 0;
|
||||||
|
|
||||||
|
pretty_print(N, total_bytes, "parse_uint16_std_fromchars",
|
||||||
|
counters::bench([&]() {
|
||||||
|
uint64_t sum = 0;
|
||||||
|
const char *p = buffer.data();
|
||||||
|
const char *pend = p + buffer.size();
|
||||||
|
for (size_t i = 0; i < N; ++i) {
|
||||||
|
uint16_t value = 0;
|
||||||
|
auto r = std::from_chars(p, pend, value);
|
||||||
|
if (r.ec != std::errc())
|
||||||
|
std::abort();
|
||||||
|
sum += value;
|
||||||
|
p = r.ptr;
|
||||||
|
if (i + 1 < N) {
|
||||||
|
if (p >= pend || *p != delimiter)
|
||||||
|
std::abort();
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (p != pend)
|
||||||
|
std::abort();
|
||||||
|
sink += sum;
|
||||||
|
}));
|
||||||
|
|
||||||
|
pretty_print(N, total_bytes, "parse_uint16_fastfloat", counters::bench([&]() {
|
||||||
|
uint64_t sum = 0;
|
||||||
|
const char *p = buffer.data();
|
||||||
|
const char *pend = p + buffer.size();
|
||||||
|
for (size_t i = 0; i < N; ++i) {
|
||||||
|
uint16_t value = 0;
|
||||||
|
auto r = fast_float::from_chars(p, pend, value);
|
||||||
|
if (r.ec != std::errc())
|
||||||
|
std::abort();
|
||||||
|
sum += value;
|
||||||
|
p = r.ptr;
|
||||||
|
if (i + 1 < N) {
|
||||||
|
if (p >= pend || *p != delimiter)
|
||||||
|
std::abort();
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (p != pend)
|
||||||
|
std::abort();
|
||||||
|
sink += sum;
|
||||||
|
}));
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
@ -31,7 +31,7 @@ fastfloat_really_inline constexpr bool has_simd_opt() noexcept {
|
|||||||
// able to optimize it well.
|
// able to optimize it well.
|
||||||
template <typename UC>
|
template <typename UC>
|
||||||
fastfloat_really_inline constexpr bool is_integer(UC c) noexcept {
|
fastfloat_really_inline constexpr bool is_integer(UC c) noexcept {
|
||||||
return !(c > UC('9') || c < UC('0'));
|
return (unsigned)(c - UC('0')) <= 9u;
|
||||||
}
|
}
|
||||||
|
|
||||||
fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) noexcept {
|
fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) noexcept {
|
||||||
@ -41,6 +41,11 @@ fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) noexcept {
|
|||||||
(val & 0x000000000000FF00) << 40 | (val & 0x00000000000000FF) << 56;
|
(val & 0x000000000000FF00) << 40 | (val & 0x00000000000000FF) << 56;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fastfloat_really_inline constexpr uint32_t byteswap_32(uint32_t val) {
|
||||||
|
return (val >> 24) | ((val >> 8) & 0x0000FF00u) | ((val << 8) & 0x00FF0000u) |
|
||||||
|
(val << 24);
|
||||||
|
}
|
||||||
|
|
||||||
// Read 8 UC into a u64. Truncates UC if not char.
|
// Read 8 UC into a u64. Truncates UC if not char.
|
||||||
template <typename UC>
|
template <typename UC>
|
||||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
|
||||||
@ -62,6 +67,25 @@ read8_to_u64(UC const *chars) noexcept {
|
|||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read 4 UC into a u32. Truncates UC if not char.
|
||||||
|
template <typename UC>
|
||||||
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint32_t
|
||||||
|
read4_to_u32(UC const *chars) {
|
||||||
|
if (cpp20_and_in_constexpr() || !std::is_same<UC, char>::value) {
|
||||||
|
uint32_t val = 0;
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
val |= uint32_t(uint8_t(*chars)) << (i * 8);
|
||||||
|
++chars;
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
uint32_t val;
|
||||||
|
::memcpy(&val, chars, sizeof(uint32_t));
|
||||||
|
#if FASTFLOAT_IS_BIG_ENDIAN == 1
|
||||||
|
val = byteswap_32(val);
|
||||||
|
#endif
|
||||||
|
return val;
|
||||||
|
}
|
||||||
#ifdef FASTFLOAT_SSE2
|
#ifdef FASTFLOAT_SSE2
|
||||||
|
|
||||||
fastfloat_really_inline uint64_t simd_read8_to_u64(__m128i const &data) {
|
fastfloat_really_inline uint64_t simd_read8_to_u64(__m128i const &data) {
|
||||||
@ -140,6 +164,18 @@ is_made_of_eight_digits_fast(uint64_t val) noexcept {
|
|||||||
0x8080808080808080));
|
0x8080808080808080));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fastfloat_really_inline constexpr bool
|
||||||
|
is_made_of_four_digits_fast(uint32_t val) noexcept {
|
||||||
|
return !((((val + 0x46464646) | (val - 0x30303030)) & 0x80808080));
|
||||||
|
}
|
||||||
|
|
||||||
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint32_t
|
||||||
|
parse_four_digits_unrolled(uint32_t val) noexcept {
|
||||||
|
val -= 0x30303030;
|
||||||
|
val = (val * 10) + (val >> 8);
|
||||||
|
return (((val & 0x00FF00FF) * 0x00640001) >> 16) & 0xFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef FASTFLOAT_HAS_SIMD
|
#ifdef FASTFLOAT_HAS_SIMD
|
||||||
|
|
||||||
// Call this if chars might not be 8 digits.
|
// Call this if chars might not be 8 digits.
|
||||||
@ -558,97 +594,145 @@ parse_int_string(UC const *p, UC const *pend, T &value,
|
|||||||
auto const *const start_digits = p;
|
auto const *const start_digits = p;
|
||||||
|
|
||||||
FASTFLOAT_IF_CONSTEXPR17((std::is_same<T, std::uint8_t>::value)) {
|
FASTFLOAT_IF_CONSTEXPR17((std::is_same<T, std::uint8_t>::value)) {
|
||||||
const auto len = static_cast<am_digits>(pend - p);
|
if (options.base == 10) {
|
||||||
if (len == 0) {
|
const auto len = static_cast<am_digits>(pend - p);
|
||||||
if (has_leading_zeros) {
|
if (len == 0) {
|
||||||
value = 0;
|
if (has_leading_zeros) {
|
||||||
answer.ec = std::errc();
|
value = 0;
|
||||||
answer.ptr = p;
|
answer.ec = std::errc();
|
||||||
} else {
|
answer.ptr = p;
|
||||||
answer.ec = std::errc::invalid_argument;
|
} else {
|
||||||
answer.ptr = first;
|
answer.ec = std::errc::invalid_argument;
|
||||||
}
|
answer.ptr = first;
|
||||||
return answer;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t digits;
|
|
||||||
|
|
||||||
#if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED && FASTFLOAT_HAS_BIT_CAST
|
|
||||||
if (std::is_constant_evaluated()) {
|
|
||||||
uint8_t str[4];
|
|
||||||
for (uint_fast8_t j = 0; j != 4 && j != len; ++j) {
|
|
||||||
str[j] = static_cast<uint8_t>(p[j]);
|
|
||||||
}
|
|
||||||
digits = bit_cast<uint32_t>(str);
|
|
||||||
#if FASTFLOAT_IS_BIG_ENDIAN
|
|
||||||
digits = byteswap(digits);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if (false) {
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
else if (len >= 4) {
|
|
||||||
std::memcpy(&digits, p, 4);
|
|
||||||
#if FASTFLOAT_IS_BIG_ENDIAN
|
|
||||||
digits = byteswap(digits);
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
uint32_t b0 = static_cast<uint8_t>(p[0]);
|
|
||||||
uint32_t b1 = (len > 1) ? static_cast<uint8_t>(p[1]) : 0xFFu;
|
|
||||||
uint32_t b2 = (len > 2) ? static_cast<uint8_t>(p[2]) : 0xFFu;
|
|
||||||
uint32_t b3 = 0xFFu;
|
|
||||||
#if FASTFLOAT_IS_BIG_ENDIAN
|
|
||||||
digits = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
|
|
||||||
#else
|
|
||||||
digits = b0 | (b1 << 8) | (b2 << 16) | (b3 << 24);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint32_t magic =
|
|
||||||
((digits + 0x46464646u) | (digits - 0x30303030u)) & 0x80808080u;
|
|
||||||
const auto tz = countr_zero_32(magic); // 7, 15, 23, 31, or 32
|
|
||||||
am_digits nd = (tz >> 3);
|
|
||||||
nd = std::min(nd, len);
|
|
||||||
if (nd == 0) {
|
|
||||||
if (has_leading_zeros) {
|
|
||||||
value = 0;
|
|
||||||
answer.ec = std::errc();
|
|
||||||
answer.ptr = p;
|
|
||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
answer.ec = std::errc::invalid_argument;
|
|
||||||
answer.ptr = first;
|
uint32_t digits;
|
||||||
return answer;
|
|
||||||
}
|
#if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED && FASTFLOAT_HAS_BIT_CAST
|
||||||
if (nd > 3) {
|
if (std::is_constant_evaluated()) {
|
||||||
const UC *q = p + nd;
|
uint8_t str[4];
|
||||||
am_digits rem = len - nd;
|
for (uint_fast8_t j = 0; j != 4 && j != len; ++j) {
|
||||||
while (rem) {
|
str[j] = static_cast<uint8_t>(p[j]);
|
||||||
if (*q < UC('0') || *q > UC('9'))
|
}
|
||||||
break;
|
digits = bit_cast<uint32_t>(str);
|
||||||
++q;
|
#if FASTFLOAT_IS_BIG_ENDIAN
|
||||||
--rem;
|
digits = byteswap_32(digits);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (false) {
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
else if (len >= 4) {
|
||||||
|
::memcpy(&digits, p, 4);
|
||||||
|
#if FASTFLOAT_IS_BIG_ENDIAN
|
||||||
|
digits = byteswap_32(digits);
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
uint32_t b0 = static_cast<uint8_t>(p[0]);
|
||||||
|
uint32_t b1 = (len > 1) ? static_cast<uint8_t>(p[1]) : 0xFFu;
|
||||||
|
uint32_t b2 = (len > 2) ? static_cast<uint8_t>(p[2]) : 0xFFu;
|
||||||
|
uint32_t b3 = 0xFFu;
|
||||||
|
digits = b0 | (b1 << 8) | (b2 << 16) | (b3 << 24);
|
||||||
}
|
}
|
||||||
answer.ec = std::errc::result_out_of_range;
|
|
||||||
answer.ptr = q;
|
|
||||||
return answer;
|
|
||||||
}
|
|
||||||
|
|
||||||
digits ^= 0x30303030u;
|
uint32_t magic =
|
||||||
digits <<= ((4 - nd) * 8);
|
((digits + 0x46464646u) | (digits - 0x30303030u)) & 0x80808080u;
|
||||||
|
uint32_t tz = (uint32_t)countr_zero_32(magic); // 7, 15, 23, 31, or 32
|
||||||
|
uint32_t nd = (tz == 32) ? 4 : (tz >> 3);
|
||||||
|
nd = (uint32_t)std::min((size_t)nd, len);
|
||||||
|
if (nd == 0) {
|
||||||
|
if (has_leading_zeros) {
|
||||||
|
value = 0;
|
||||||
|
answer.ec = std::errc();
|
||||||
|
answer.ptr = p;
|
||||||
|
return answer;
|
||||||
|
}
|
||||||
|
answer.ec = std::errc::invalid_argument;
|
||||||
|
answer.ptr = first;
|
||||||
|
return answer;
|
||||||
|
}
|
||||||
|
if (nd > 3) {
|
||||||
|
const UC *q = p + nd;
|
||||||
|
size_t rem = len - nd;
|
||||||
|
while (rem) {
|
||||||
|
if (*q < UC('0') || *q > UC('9'))
|
||||||
|
break;
|
||||||
|
++q;
|
||||||
|
--rem;
|
||||||
|
}
|
||||||
|
answer.ec = std::errc::result_out_of_range;
|
||||||
|
answer.ptr = q;
|
||||||
|
return answer;
|
||||||
|
}
|
||||||
|
|
||||||
const uint32_t check = ((digits >> 24) & 0xff) | ((digits >> 8) & 0xff00) |
|
digits ^= 0x30303030u;
|
||||||
((digits << 8) & 0xff0000);
|
digits <<= ((4 - nd) * 8);
|
||||||
if (check > 0x00020505) {
|
|
||||||
answer.ec = std::errc::result_out_of_range;
|
uint32_t check = ((digits >> 24) & 0xff) | ((digits >> 8) & 0xff00) |
|
||||||
|
((digits << 8) & 0xff0000);
|
||||||
|
if (check > 0x00020505) {
|
||||||
|
answer.ec = std::errc::result_out_of_range;
|
||||||
|
answer.ptr = p + nd;
|
||||||
|
return answer;
|
||||||
|
}
|
||||||
|
value = (uint8_t)((0x640a01 * digits) >> 24);
|
||||||
|
answer.ec = std::errc();
|
||||||
answer.ptr = p + nd;
|
answer.ptr = p + nd;
|
||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
value = static_cast<uint8_t>((0x640a01 * digits) >> 24);
|
}
|
||||||
answer.ec = std::errc();
|
|
||||||
answer.ptr = p + nd;
|
FASTFLOAT_IF_CONSTEXPR17((std::is_same<T, std::uint16_t>::value)) {
|
||||||
return answer;
|
if (options.base == 10) {
|
||||||
|
const size_t len = size_t(pend - p);
|
||||||
|
if (len == 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len >= 4) {
|
||||||
|
uint32_t digits = read4_to_u32(p);
|
||||||
|
if (is_made_of_four_digits_fast(digits)) {
|
||||||
|
uint32_t v = parse_four_digits_unrolled(digits);
|
||||||
|
if (len >= 5 && is_integer(p[4])) {
|
||||||
|
v = v * 10 + uint32_t(p[4] - '0');
|
||||||
|
if (len >= 6 && is_integer(p[5])) {
|
||||||
|
answer.ec = std::errc::result_out_of_range;
|
||||||
|
const UC *q = p + 5;
|
||||||
|
while (q != pend && is_integer(*q)) {
|
||||||
|
q++;
|
||||||
|
}
|
||||||
|
answer.ptr = q;
|
||||||
|
return answer;
|
||||||
|
}
|
||||||
|
if (v > 65535) {
|
||||||
|
answer.ec = std::errc::result_out_of_range;
|
||||||
|
answer.ptr = p + 5;
|
||||||
|
return answer;
|
||||||
|
}
|
||||||
|
value = uint16_t(v);
|
||||||
|
answer.ec = std::errc();
|
||||||
|
answer.ptr = p + 5;
|
||||||
|
return answer;
|
||||||
|
}
|
||||||
|
// 4 digits
|
||||||
|
value = uint16_t(v);
|
||||||
|
answer.ec = std::errc();
|
||||||
|
answer.ptr = p + 4;
|
||||||
|
return answer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse digits
|
// Parse digits
|
||||||
|
|||||||
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
#define FASTFLOAT_VERSION_MAJOR 8
|
#define FASTFLOAT_VERSION_MAJOR 8
|
||||||
#define FASTFLOAT_VERSION_MINOR 2
|
#define FASTFLOAT_VERSION_MINOR 2
|
||||||
#define FASTFLOAT_VERSION_PATCH 1
|
#define FASTFLOAT_VERSION_PATCH 2
|
||||||
|
|
||||||
#define FASTFLOAT_STRINGIZE_IMPL(x) #x
|
#define FASTFLOAT_STRINGIZE_IMPL(x) #x
|
||||||
#define FASTFLOAT_STRINGIZE(x) FASTFLOAT_STRINGIZE_IMPL(x)
|
#define FASTFLOAT_STRINGIZE(x) FASTFLOAT_STRINGIZE_IMPL(x)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user