Add ability to convert larger numbers without fast_float, write unit tests

This commit is contained in:
ado 2023-08-08 14:11:51 +02:00
parent b9d2c2aad9
commit 672b89b213
4 changed files with 60 additions and 19 deletions

View File

@ -1,13 +1,13 @@
#pragma once #pragma once
#include "type_traits.hpp" #include "type_traits.hpp"
#include <charconv>
#include <cstring> #include <cstring>
#include <functional> #include <functional>
#include <optional> #include <optional>
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <variant> #include <variant>
#include <charconv>
#ifndef SSP_DISABLE_FAST_FLOAT #ifndef SSP_DISABLE_FAST_FLOAT
#include <fast_float/fast_float.h> #include <fast_float/fast_float.h>
@ -42,16 +42,23 @@ std::enable_if_t<std::is_floating_point_v<T>, std::optional<T>> to_num(
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* const begin, const char* const end) { const char* const begin, const char* const end) {
static_assert(!std::is_same_v<T, long double>,
"Conversion to long double is disabled");
constexpr static auto buff_max = 64; constexpr static auto buff_max = 64;
char buff[buff_max]; char short_buff[buff_max];
size_t string_range = std::distance(begin, end); size_t string_range = std::distance(begin, end);
std::string long_buff;
char* buff;
if (string_range > buff_max) { if (string_range > buff_max) {
return std::nullopt; long_buff = std::string{begin, end};
} buff = long_buff.data();
} else {
std::copy_n(begin, string_range, buff); buff = short_buff;
buff[string_range] = '\0'; buff[string_range] = '\0';
std::copy_n(begin, string_range, buff);
}
T ret; T ret;
char* parse_end = nullptr; char* parse_end = nullptr;
@ -60,8 +67,6 @@ std::enable_if_t<std::is_floating_point_v<T>, std::optional<T>> to_num(
ret = std::strtof(buff, &parse_end); ret = std::strtof(buff, &parse_end);
} else if constexpr (std::is_same_v<T, double>) { } else if constexpr (std::is_same_v<T, double>) {
ret = std::strtod(buff, &parse_end); ret = std::strtod(buff, &parse_end);
} else if constexpr (std::is_same_v<T, long double>) {
ret = std::strtold(buff, &parse_end);
} }
if (parse_end != buff + string_range) { if (parse_end != buff + string_range) {

View File

@ -63,11 +63,6 @@ TEST_CASE("extract test functions for numbers with invalid inputs") {
// random input for float // random input for float
CHECK_INVALID_CONVERSION("xxx1", float); CHECK_INVALID_CONVERSION("xxx1", float);
// number too big
CHECK_INVALID_CONVERSION((std::string(40, '1') + "." +
std::string(40, '2')),
double);
// random input for int // random input for int
CHECK_INVALID_CONVERSION("xxx1", int); CHECK_INVALID_CONVERSION("xxx1", int);
@ -280,3 +275,20 @@ TEST_CASE("extract test functions for std::variant") {
} }
} }
} }
TEST_CASE("extract test with long number string") {
{
std::string string_num =
std::string(20, '1') + "." + std::string(20, '2');
CHECK_FLOATING_CONVERSION_LONG_NUMBER(string_num, float, stof);
CHECK_FLOATING_CONVERSION_LONG_NUMBER(string_num, double, stod);
}
{
std::string string_num =
std::string(50, '1') + "." + std::string(50, '2');
CHECK_FLOATING_CONVERSION_LONG_NUMBER(string_num, double, stod);
}
}

View File

@ -29,11 +29,6 @@ TEST_CASE("extract test functions for numbers with invalid inputs without fast "
// random input for float // random input for float
CHECK_INVALID_CONVERSION("xxx1", float); CHECK_INVALID_CONVERSION("xxx1", float);
// number too big
CHECK_INVALID_CONVERSION((std::string(40, '1') + "." +
std::string(40, '2')),
double);
} }
TEST_CASE("extract test functions for std::variant without fast float") { TEST_CASE("extract test functions for std::variant without fast float") {
@ -135,3 +130,20 @@ TEST_CASE("extract test functions for std::variant without fast float") {
} }
} }
} }
TEST_CASE("extract test with long number string without fast float") {
{
std::string string_num =
std::string(20, '1') + "." + std::string(20, '2');
CHECK_FLOATING_CONVERSION_LONG_NUMBER(string_num, float, stof);
CHECK_FLOATING_CONVERSION_LONG_NUMBER(string_num, double, stod);
}
{
std::string string_num =
std::string(50, '1') + "." + std::string(50, '2');
CHECK_FLOATING_CONVERSION_LONG_NUMBER(string_num, double, stod);
}
}

View File

@ -1,10 +1,10 @@
#pragma once #pragma once
#include <ctime> #include <ctime>
#include <filesystem>
#include <iomanip> #include <iomanip>
#include <sstream> #include <sstream>
#include <string> #include <string>
#include <vector> #include <vector>
#include <filesystem>
#ifdef CMAKE_GITHUB_CI #ifdef CMAKE_GITHUB_CI
#include <doctest/doctest.h> #include <doctest/doctest.h>
@ -75,6 +75,18 @@ struct unique_file_name {
CHECK_LT(std::abs(t.value() - type(-input)), eps); \ CHECK_LT(std::abs(t.value() - type(-input)), eps); \
} }
#define CHECK_FLOATING_CONVERSION_LONG_NUMBER(STRING_NUMBER, TYPE, CONVERTER) \
{ \
auto begin = STRING_NUMBER.c_str(); \
auto end = begin + STRING_NUMBER.size(); \
\
auto number = ss::to_num<TYPE>(begin, end); \
REQUIRE(number.has_value()); \
\
auto expected_number = CONVERTER(STRING_NUMBER); \
CHECK_EQ(number.value(), expected_number); \
}
#define CHECK_INVALID_CONVERSION(input, type) \ #define CHECK_INVALID_CONVERSION(input, type) \
{ \ { \
std::string s = input; \ std::string s = input; \