mirror of
https://github.com/red0124/ssp.git
synced 2025-01-23 13:05:20 +01:00
Merge pull request #6 from red0124/feature/fast_float
Feature/fast float
This commit is contained in:
commit
77199f0145
@ -16,6 +16,16 @@ if(ssp_INCLUDE_WITHOUT_SYSTEM)
|
|||||||
set(ssp_warning_guard "")
|
set(ssp_warning_guard "")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# ---- Dependencies ----
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
fast_float
|
||||||
|
GIT_REPOSITORY https://github.com/red0124/fast_float.git
|
||||||
|
GIT_TAG origin/meson
|
||||||
|
GIT_SHALLOW TRUE)
|
||||||
|
|
||||||
|
FetchContent_MakeAvailable(fast_float)
|
||||||
|
|
||||||
# ---- Declare library ----
|
# ---- Declare library ----
|
||||||
|
|
||||||
add_library(ssp INTERFACE)
|
add_library(ssp INTERFACE)
|
||||||
@ -26,6 +36,7 @@ target_include_directories(
|
|||||||
${ssp_warning_guard}
|
${ssp_warning_guard}
|
||||||
INTERFACE
|
INTERFACE
|
||||||
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
|
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
|
||||||
|
fast_float
|
||||||
)
|
)
|
||||||
|
|
||||||
target_compile_features(ssp INTERFACE cxx_std_17)
|
target_compile_features(ssp INTERFACE cxx_std_17)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
A header only "csv" parser which is fast and versatile with modern C++ api. Requires compiler with C++17 support.
|
A header only "csv" parser which is fast and versatile with modern C++ api. Requires compiler with C++17 support.
|
||||||
|
|
||||||
Conversion for numeric values taken from [Oliver Schönrock](https://gist.github.com/oschonrock/67fc870ba067ebf0f369897a9d52c2dd) .
|
Conversion for floating point values invoked using [fast-float](https://github.com/fastfloat/fast_float) .
|
||||||
Function traits taken from [qt-creator](https://code.woboq.org/qt5/qt-creator/src/libs/utils/functiontraits.h.html) .
|
Function traits taken from [qt-creator](https://code.woboq.org/qt5/qt-creator/src/libs/utils/functiontraits.h.html) .
|
||||||
|
|
||||||
# Example
|
# Example
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "type_traits.hpp"
|
#include "type_traits.hpp"
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <fast_float/fast_float.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
@ -14,104 +15,17 @@ namespace ss {
|
|||||||
////////////////
|
////////////////
|
||||||
// number converters
|
// number converters
|
||||||
////////////////
|
////////////////
|
||||||
template <typename T>
|
|
||||||
std::enable_if_t<std::is_floating_point_v<T>, T> pow10(int n) {
|
|
||||||
T ret = 1.0;
|
|
||||||
T r = 10.0;
|
|
||||||
if (n < 0) {
|
|
||||||
n = -n;
|
|
||||||
r = 0.1;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (n) {
|
|
||||||
if (n & 1) {
|
|
||||||
ret *= r;
|
|
||||||
}
|
|
||||||
r *= r;
|
|
||||||
n >>= 1;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO not working with large number of digits
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::enable_if_t<std::is_floating_point_v<T>, std::optional<T>> to_num(
|
std::enable_if_t<std::is_floating_point_v<T>, std::optional<T>> to_num(
|
||||||
const char* begin, const char* const end) {
|
const char* begin, const char* const end) {
|
||||||
if (begin == end) {
|
T ret;
|
||||||
|
auto answer = fast_float::from_chars(begin, end, ret);
|
||||||
|
|
||||||
|
if (answer.ec != std::errc() || answer.ptr != end) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
int sign = 1;
|
return ret;
|
||||||
T int_part = 0.0;
|
|
||||||
T frac_part = 0.0;
|
|
||||||
bool has_frac = false;
|
|
||||||
bool has_exp = false;
|
|
||||||
|
|
||||||
// +/- sign
|
|
||||||
if (*begin == '-') {
|
|
||||||
++begin;
|
|
||||||
sign = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (begin != end) {
|
|
||||||
if (*begin >= '0' && *begin <= '9') {
|
|
||||||
int_part = int_part * 10 + (*begin - '0');
|
|
||||||
} else if (*begin == '.') {
|
|
||||||
has_frac = true;
|
|
||||||
++begin;
|
|
||||||
break;
|
|
||||||
} else if (*begin == 'e') {
|
|
||||||
has_exp = true;
|
|
||||||
++begin;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
++begin;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (has_frac) {
|
|
||||||
T frac_exp = 0.1;
|
|
||||||
|
|
||||||
while (begin != end) {
|
|
||||||
if (*begin >= '0' && *begin <= '9') {
|
|
||||||
frac_part += frac_exp * (*begin - '0');
|
|
||||||
frac_exp *= 0.1;
|
|
||||||
} else if (*begin == 'e') {
|
|
||||||
has_exp = true;
|
|
||||||
++begin;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
++begin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// parsing exponent part
|
|
||||||
T exp_part = 1.0;
|
|
||||||
if (begin != end && has_exp) {
|
|
||||||
int exp_sign = 1;
|
|
||||||
if (*begin == '-') {
|
|
||||||
exp_sign = -1;
|
|
||||||
++begin;
|
|
||||||
} else if (*begin == '+') {
|
|
||||||
++begin;
|
|
||||||
}
|
|
||||||
|
|
||||||
int e = 0;
|
|
||||||
while (begin != end && *begin >= '0' && *begin <= '9') {
|
|
||||||
e = e * 10 + *begin - '0';
|
|
||||||
++begin;
|
|
||||||
}
|
|
||||||
|
|
||||||
exp_part = pow10<T>(exp_sign * e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (begin != end) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sign * (int_part + frac_part) * exp_part;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::optional<short> from_char(char c) {
|
inline std::optional<short> from_char(char c) {
|
||||||
@ -249,7 +163,7 @@ bool shift_and_add_overflow(T& value, T digit, F add_last_digit_owerflow) {
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#warning "use clang or gcc!!!"
|
#warning "Use clang or gcc if possible."
|
||||||
template <typename T, typename U>
|
template <typename T, typename U>
|
||||||
bool shift_and_add_overflow(T& value, T digit, U is_negative) {
|
bool shift_and_add_overflow(T& value, T digit, U is_negative) {
|
||||||
digit = (is_negative) ? -digit : digit;
|
digit = (is_negative) ? -digit : digit;
|
||||||
|
@ -4,5 +4,12 @@ project('ssp', 'cpp',
|
|||||||
'cpp_std=c++17',
|
'cpp_std=c++17',
|
||||||
'buildtype=debugoptimized'])
|
'buildtype=debugoptimized'])
|
||||||
|
|
||||||
includes = include_directories('include')
|
fast_float_sub = subproject('fast_float')
|
||||||
|
fast_float_dep = fast_float_sub.get_variable('fast_float_dep')
|
||||||
|
|
||||||
|
ssp_dep = declare_dependency(
|
||||||
|
include_directories: include_directories('include'),
|
||||||
|
dependencies: fast_float_dep
|
||||||
|
)
|
||||||
|
|
||||||
subdir('test')
|
subdir('test')
|
||||||
|
3
subprojects/fast_float.wrap
Normal file
3
subprojects/fast_float.wrap
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[wrap-git]
|
||||||
|
url = https://github.com/red0124/fast_float
|
||||||
|
revision = meson
|
@ -30,7 +30,7 @@ enable_testing()
|
|||||||
|
|
||||||
foreach(name IN ITEMS test_splitter test_parser test_converter test_extractions)
|
foreach(name IN ITEMS test_splitter test_parser test_converter test_extractions)
|
||||||
add_executable("${name}" "${name}.cpp")
|
add_executable("${name}" "${name}.cpp")
|
||||||
target_link_libraries("${name}" PRIVATE ssp::ssp doctest::doctest)
|
target_link_libraries("${name}" PRIVATE ssp::ssp fast_float doctest::doctest)
|
||||||
target_compile_definitions("${name}" PRIVATE
|
target_compile_definitions("${name}" PRIVATE
|
||||||
DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN CMAKE_GITHUB_CI)
|
DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN CMAKE_GITHUB_CI)
|
||||||
doctest_discover_tests("${name}")
|
doctest_discover_tests("${name}")
|
||||||
|
@ -9,11 +9,11 @@ test_sources = files([
|
|||||||
doctest_proj = subproject('doctest')
|
doctest_proj = subproject('doctest')
|
||||||
doctest_dep = doctest_proj.get_variable('doctest_dep')
|
doctest_dep = doctest_proj.get_variable('doctest_dep')
|
||||||
|
|
||||||
test_exe = executable('test_ssp',
|
test_exe = executable(
|
||||||
sources: test_sources,
|
'test_ssp',
|
||||||
dependencies: doctest_dep,
|
sources: test_sources,
|
||||||
include_directories: includes,
|
dependencies: [doctest_dep, ssp_dep],
|
||||||
cpp_args: '-lstdc++fs'
|
cpp_args: '-lstdc++fs'
|
||||||
)
|
)
|
||||||
|
|
||||||
test('tests_ssp', test_exe)
|
test('test_ssp', test_exe)
|
||||||
|
@ -2,11 +2,9 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <ss/extract.hpp>
|
#include <ss/extract.hpp>
|
||||||
|
|
||||||
constexpr auto eps = 0.000001;
|
|
||||||
using ld = long double;
|
|
||||||
|
|
||||||
#define CHECK_FLOATING_CONVERSION(input, type) \
|
#define CHECK_FLOATING_CONVERSION(input, type) \
|
||||||
{ \
|
{ \
|
||||||
|
auto eps = std::numeric_limits<type>::min(); \
|
||||||
std::string s = #input; \
|
std::string s = #input; \
|
||||||
auto t = ss::to_num<type>(s.c_str(), s.c_str() + s.size()); \
|
auto t = ss::to_num<type>(s.c_str(), s.c_str() + s.size()); \
|
||||||
REQUIRE(t.has_value()); \
|
REQUIRE(t.has_value()); \
|
||||||
@ -14,6 +12,7 @@ using ld = long double;
|
|||||||
} \
|
} \
|
||||||
{ \
|
{ \
|
||||||
/* check negative too */ \
|
/* check negative too */ \
|
||||||
|
auto eps = std::numeric_limits<type>::min(); \
|
||||||
auto s = std::string("-") + #input; \
|
auto s = std::string("-") + #input; \
|
||||||
auto t = ss::to_num<type>(s.c_str(), s.c_str() + s.size()); \
|
auto t = ss::to_num<type>(s.c_str(), s.c_str() + s.size()); \
|
||||||
REQUIRE(t.has_value()); \
|
REQUIRE(t.has_value()); \
|
||||||
@ -23,23 +22,18 @@ using ld = long double;
|
|||||||
TEST_CASE("testing extract functions for floating point values") {
|
TEST_CASE("testing extract functions for floating point values") {
|
||||||
CHECK_FLOATING_CONVERSION(123.456, float);
|
CHECK_FLOATING_CONVERSION(123.456, float);
|
||||||
CHECK_FLOATING_CONVERSION(123.456, double);
|
CHECK_FLOATING_CONVERSION(123.456, double);
|
||||||
CHECK_FLOATING_CONVERSION(123.456, ld);
|
|
||||||
|
|
||||||
CHECK_FLOATING_CONVERSION(69, float);
|
CHECK_FLOATING_CONVERSION(69, float);
|
||||||
CHECK_FLOATING_CONVERSION(69, double);
|
CHECK_FLOATING_CONVERSION(69, double);
|
||||||
CHECK_FLOATING_CONVERSION(69, ld);
|
|
||||||
|
|
||||||
CHECK_FLOATING_CONVERSION(420., float);
|
CHECK_FLOATING_CONVERSION(420., float);
|
||||||
CHECK_FLOATING_CONVERSION(420., double);
|
CHECK_FLOATING_CONVERSION(420., double);
|
||||||
CHECK_FLOATING_CONVERSION(420., ld);
|
|
||||||
|
|
||||||
CHECK_FLOATING_CONVERSION(0.123, float);
|
CHECK_FLOATING_CONVERSION(0.123, float);
|
||||||
CHECK_FLOATING_CONVERSION(0.123, double);
|
CHECK_FLOATING_CONVERSION(0.123, double);
|
||||||
CHECK_FLOATING_CONVERSION(0.123, ld);
|
|
||||||
|
|
||||||
CHECK_FLOATING_CONVERSION(123e4, float);
|
CHECK_FLOATING_CONVERSION(123e4, float);
|
||||||
CHECK_FLOATING_CONVERSION(123e4, double);
|
CHECK_FLOATING_CONVERSION(123e4, double);
|
||||||
CHECK_FLOATING_CONVERSION(123e4, ld);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CHECK_DECIMAL_CONVERSION(input, type) \
|
#define CHECK_DECIMAL_CONVERSION(input, type) \
|
||||||
|
Loading…
Reference in New Issue
Block a user