Доработана конвертация в числа. Обновлена версия и результаты бенчмарков.

This commit is contained in:
Aleksandr Orefkov 2025-11-26 22:15:50 +03:00
parent 573ffd247e
commit 13b31fdc4b
13 changed files with 4449 additions and 4279 deletions

View File

@ -5,8 +5,8 @@ include(FetchContent)
project( project(
simstr simstr
VERSION 1.2.5 VERSION 1.2.6
DESCRIPTION "Yet another string library" DESCRIPTION "Yet another modern C++ string library"
HOMEPAGE_URL "https://github.com/orefkov/simstr" HOMEPAGE_URL "https://github.com/orefkov/simstr"
LANGUAGES CXX LANGUAGES CXX
) )

View File

@ -176,9 +176,6 @@ void ToIntStr0(benchmark::State& state, const std::string& s, int c) {
} }
void ToIntFromChars10(benchmark::State& state, const std::string_view& s, int c) { void ToIntFromChars10(benchmark::State& state, const std::string_view& s, int c) {
//#ifdef __EMSCRIPTEN__
//state.SkipWithError("not implemented");
//#else
for (auto _: state) { for (auto _: state) {
int res = 0; int res = 0;
std::from_chars(s.data(), s.data() + s.size(), res, 10); std::from_chars(s.data(), s.data() + s.size(), res, 10);
@ -190,13 +187,9 @@ void ToIntFromChars10(benchmark::State& state, const std::string_view& s, int c)
#endif #endif
benchmark::DoNotOptimize(res); benchmark::DoNotOptimize(res);
} }
//#endif
} }
void ToIntFromChars16(benchmark::State& state, const std::string_view& s, int c) { void ToIntFromChars16(benchmark::State& state, const std::string_view& s, int c) {
//#ifdef __EMSCRIPTEN__
// state.SkipWithError("not implemented");
//#else
for (auto _: state) { for (auto _: state) {
int res = 0; int res = 0;
std::from_chars(s.data(), s.data() + s.size(), res, 16); std::from_chars(s.data(), s.data() + s.size(), res, 16);
@ -208,13 +201,12 @@ void ToIntFromChars16(benchmark::State& state, const std::string_view& s, int c)
#endif #endif
benchmark::DoNotOptimize(res); benchmark::DoNotOptimize(res);
} }
//#endif
} }
template<typename T> template<typename T>
void ToIntSimStr10(benchmark::State& state, T t, int c) { void ToIntSimStr10(benchmark::State& state, T t, int c) {
for (auto _: state) { for (auto _: state) {
int res = std::get<0>(t. template to_int<int, true, 10, false, false>()); int res = t. template to_int<int, true, 10, false, false>().value;
#ifdef CHECK_RESULT #ifdef CHECK_RESULT
if (res != c) { if (res != c) {
state.SkipWithError("not equal"); state.SkipWithError("not equal");
@ -229,7 +221,7 @@ void ToIntSimStr10(benchmark::State& state, T t, int c) {
template<typename T> template<typename T>
void ToIntSimStr16(benchmark::State& state, T t, int c) { void ToIntSimStr16(benchmark::State& state, T t, int c) {
for (auto _: state) { for (auto _: state) {
int res = std::get<0>(t. template to_int<int, true, 16, false, false>()); int res = t. template to_int<int, true, 16, false, false>().value;
#ifdef CHECK_RESULT #ifdef CHECK_RESULT
if (res != c) { if (res != c) {
state.SkipWithError("not equal"); state.SkipWithError("not equal");
@ -244,7 +236,7 @@ void ToIntSimStr16(benchmark::State& state, T t, int c) {
template<typename T> template<typename T>
void ToIntSimStr0(benchmark::State& state, T t, int c) { void ToIntSimStr0(benchmark::State& state, T t, int c) {
for (auto _: state) { for (auto _: state) {
int res = std::get<0>(t. template to_int<int>()); int res = t. template to_int<int>().value;
#ifdef CHECK_RESULT #ifdef CHECK_RESULT
if (res != c) { if (res != c) {
state.SkipWithError("not equal"); state.SkipWithError("not equal");
@ -258,7 +250,7 @@ void ToIntSimStr0(benchmark::State& state, T t, int c) {
void ToIntNoOverflow(benchmark::State& state, ssa t, int c) { void ToIntNoOverflow(benchmark::State& state, ssa t, int c) {
for (auto _: state) { for (auto _: state) {
int res = std::get<0>(t.to_int<int, false>()); int res = t.to_int<int, false>().value;
#ifdef CHECK_RESULT #ifdef CHECK_RESULT
if (res != c) { if (res != c) {
state.SkipWithError("not equal"); state.SkipWithError("not equal");
@ -287,6 +279,63 @@ BENCHMARK_CAPTURE(ToIntStr0, , std::string{" 123456789"}, 123456789)
BENCHMARK_CAPTURE(ToIntSimStr0, , stringa{" 123456789"}, 123456789) ->Name("stringa s = \" 123456789\"; int res = s.to_int<int>; // Check overflow"); BENCHMARK_CAPTURE(ToIntSimStr0, , stringa{" 123456789"}, 123456789) ->Name("stringa s = \" 123456789\"; int res = s.to_int<int>; // Check overflow");
BENCHMARK_CAPTURE(ToIntNoOverflow, , ssa{" 123456789"}, 123456789) ->Name("ssa s = \" 123456789\"; int res = s.to_int<int, false>; // No check overflow"); BENCHMARK_CAPTURE(ToIntNoOverflow, , ssa{" 123456789"}, 123456789) ->Name("ssa s = \" 123456789\"; int res = s.to_int<int, false>; // No check overflow");
void ToDoubleStr(benchmark::State& state, const std::string& s, double c) {
for (auto _: state) {
char* ptr = nullptr;
double res = std::strtod(s.c_str(), &ptr);
if (ptr == s.c_str()) {
state.SkipWithError("not equal");
}
#ifdef CHECK_RESULT
if (res != c) {
state.SkipWithError("not equal");
break;
}
#endif
benchmark::DoNotOptimize(res);
}
}
void ToDoubleFromChars(benchmark::State& state, const std::string_view& s, double c) {
for (auto _: state) {
double res = 0;
if (std::from_chars(s.data(), s.data() + s.size(), res).ec != std::errc{}) {
state.SkipWithError("not equal");
}
#ifdef CHECK_RESULT
if (res != c) {
state.SkipWithError("not equal");
break;
}
#endif
benchmark::DoNotOptimize(res);
}
}
template<typename T>
void ToDoubleSimStr(benchmark::State& state, T t, double c) {
for (auto _: state) {
auto r = t.template to_double<false>();
if (!r) {
state.SkipWithError("not equal");
}
double res = *r;
#ifdef CHECK_RESULT
if (res != c) {
state.SkipWithError("not equal");
break;
}
#endif
benchmark::DoNotOptimize(res);
benchmark::DoNotOptimize(t);
}
}
BENCHMARK(__)->Name("----- Convert to double '1234.567e10' ---------")->Repetitions(1);
BENCHMARK_CAPTURE(ToDoubleStr, , std::string{"1234.567e10"}, 1234.567e10) ->Name("std::string s = \"1234.567e10\"; double res = std::strtod(s.c_str(), nullptr);");
BENCHMARK_CAPTURE(ToDoubleFromChars, , std::string_view{"1234.567e10"}, 1234.567e10) ->Name("std::string_view s = \"1234.567e10\"; std::from_chars(s.data(), s.data() + s.size(), res);");
BENCHMARK_CAPTURE(ToDoubleSimStr, , ssa{"1234.567e10"}, 1234.567e10) ->Name("ssa s = \"1234.567e10\"; double res = *s.to_double()");
void AppendStreamConstLiteral(benchmark::State& state) { void AppendStreamConstLiteral(benchmark::State& state) {
for (auto _: state) { for (auto _: state) {
std::string result; std::string result;

View File

@ -84,9 +84,9 @@ results_vector get_results_infos() {
} }
} }
} }
results_vector results; results_vector results;
if (fileNames.size()) { if (fileNames.size()) {
std::sort(fileNames.begin(), fileNames.end()); std::sort(fileNames.begin(), fileNames.end());
results.reserve(fileNames.size()); results.reserve(fileNames.size());
@ -95,7 +95,7 @@ results_vector get_results_infos() {
// В начале имени файла может идти число и дефис, для сортировки, уберём их // В начале имени файла может идти число и дефис, для сортировки, уберём их
// At the beginning of the file name there can be a number and a hyphen, for sorting, remove them // At the beginning of the file name there can be a number and a hyphen, for sorting, remove them
if (auto delimeter = fileName.find('-'); delimeter + 1 > 1) { if (auto delimeter = fileName.find('-'); delimeter + 1 > 1) {
if (std::get<1>(fileName(0, delimeter).to_int<unsigned, false, 10, false, false>()) == IntConvertResult::Success) { if (fileName(0, delimeter).to_int<unsigned, false, 10, false, false>().ec == IntConvertResult::Success) {
fileName.remove_prefix(delimeter + 1); fileName.remove_prefix(delimeter + 1);
} }
} }
@ -187,7 +187,7 @@ ssa extract_source_for_benchmark(ssa benchName, ssa sourceText) {
static hashStrMapA<stringa> textes; static hashStrMapA<stringa> textes;
size_t delim = benchName.find_last('/'); size_t delim = benchName.find_last('/');
if (delim != str::npos && std::get<1>(benchName(delim + 1).to_int<unsigned, false, 10, false, false>()) == IntConvertResult::Success) { if (delim != str::npos && benchName(delim + 1).to_int<unsigned, false, 10, false, false>().ec == IntConvertResult::Success) {
benchName.len = delim; benchName.len = delim;
} }
auto [it, not_exist] = textes.try_emplace(benchName); auto [it, not_exist] = textes.try_emplace(benchName);
@ -326,6 +326,9 @@ void write_benchmarks(out_t& out, const results_vector& results, ssa sourceText,
throw std::runtime_error{"Not expected end of file"}; throw std::runtime_error{"Not expected end of file"};
} }
line = splitters[idx].next(); line = splitters[idx].next();
if (line.starts_with("std::unordered_map<std::string, size_t> emplace & find std::string_view;_cv")) {
int t = 1;
}
} }
} }
if (needFooter) { if (needFooter) {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -293,6 +293,13 @@ constexpr unsigned digit_width() {
template<typename T, unsigned Base> template<typename T, unsigned Base>
constexpr unsigned max_overflow_digits = (sizeof(T) * CHAR_BIT) / digit_width<Base>(); constexpr unsigned max_overflow_digits = (sizeof(T) * CHAR_BIT) / digit_width<Base>();
template<typename T>
struct convert_result {
T value;
IntConvertResult ec;
size_t read;
};
struct int_convert { // NOLINT struct int_convert { // NOLINT
inline static const uint8_t NUMBERS[] = { inline static const uint8_t NUMBERS[] = {
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
@ -322,7 +329,7 @@ struct int_convert { // NOLINT
template<typename K, ToIntNumber T, unsigned Base, bool CheckOverflow> template<typename K, ToIntNumber T, unsigned Base, bool CheckOverflow>
requires(Base != 0) requires(Base != 0)
static std::tuple<T, IntConvertResult, size_t> parse(const K* start, const K* current, const K* end, bool negate) { static convert_result<T> parse(const K* start, const K* current, const K* end, bool negate) {
using u_type = std::make_unsigned_t<T>; using u_type = std::make_unsigned_t<T>;
#ifndef HAS_BUILTIN_OVERFLOW #ifndef HAS_BUILTIN_OVERFLOW
u_type maxMult = 0, maxAdd = 0; u_type maxMult = 0, maxAdd = 0;
@ -406,7 +413,7 @@ struct int_convert { // NOLINT
if (error == IntConvertResult::NotNumber && current > from) { if (error == IntConvertResult::NotNumber && current > from) {
error = IntConvertResult::BadSymbolAtTail; error = IntConvertResult::BadSymbolAtTail;
} }
return {result, error, current - start}; return {result, error, size_t(current - start)};
} }
public: public:
// Если Base = 0 - то пытается определить основание по префиксу 0[xX] как 16, 0 как 8, иначе 10 // Если Base = 0 - то пытается определить основание по префиксу 0[xX] как 16, 0 как 8, иначе 10
@ -415,7 +422,7 @@ public:
// If Base = -1 - then tries to determine the base by the prefix 0[xX] as 16, 0[bB] as 2, 0[oO] or 0 as 8, otherwise 10 // If Base = -1 - then tries to determine the base by the prefix 0[xX] as 16, 0[bB] as 2, 0[oO] or 0 as 8, otherwise 10
template<typename K, ToIntNumber T, unsigned Base = 0, bool CheckOverflow = true, bool SkipWs = true, bool AllowSign = true> template<typename K, ToIntNumber T, unsigned Base = 0, bool CheckOverflow = true, bool SkipWs = true, bool AllowSign = true>
requires(Base == -1 || (Base < 37 && Base != 1)) requires(Base == -1 || (Base < 37 && Base != 1))
static std::tuple<T, IntConvertResult, size_t> to_integer(const K* start, size_t len) noexcept { static convert_result<T> to_integer(const K* start, size_t len) noexcept {
const K *ptr = start, *end = ptr + len; const K *ptr = start, *end = ptr + len;
bool negate = false; bool negate = false;
if constexpr (SkipWs) { if constexpr (SkipWs) {
@ -467,13 +474,13 @@ public:
} }
return parse<K, T, 8, CheckOverflow>(start, --ptr, end, negate); return parse<K, T, 8, CheckOverflow>(start, --ptr, end, negate);
} }
return {0, IntConvertResult::Success, ptr - start}; return {0, IntConvertResult::Success, size_t(ptr - start)};
} }
return parse<K, T, 10, CheckOverflow>(start, ptr, end, negate); return parse<K, T, 10, CheckOverflow>(start, ptr, end, negate);
} else } else
return parse<K, T, Base, CheckOverflow>(start, ptr, end, negate); return parse<K, T, Base, CheckOverflow>(start, ptr, end, negate);
} }
return {0, IntConvertResult::NotNumber, ptr - start}; return {0, IntConvertResult::NotNumber, size_t(ptr - start)};
} }
}; };
@ -1439,7 +1446,7 @@ public:
* - в остальных случаях 10. * - в остальных случаях 10.
* @tparam SkipWs - пропускать пробельные символы в начале строки. Пропускаются все символы с ASCII кодами <= 32. * @tparam SkipWs - пропускать пробельные символы в начале строки. Пропускаются все символы с ASCII кодами <= 32.
* @tparam AllowSign - допустим ли знак '+' перед числом. * @tparam AllowSign - допустим ли знак '+' перед числом.
* @return std::tuple<T, IntConvertResult, size_t> - кортеж из полученного числа, успешности преобразования и количестве обработанных символов. * @return convert_result<T> - кортеж из полученного числа, успешности преобразования и количестве обработанных символов.
* @en @brief Convert a string to a number of the given type. * @en @brief Convert a string to a number of the given type.
* @tparam T - the desired number type. * @tparam T - the desired number type.
* @tparam CheckOverflow - check for overflow. * @tparam CheckOverflow - check for overflow.
@ -1452,10 +1459,10 @@ public:
* - in other cases 10. * - in other cases 10.
* @tparam SkipWs - skip whitespace characters at the beginning of the line. All characters with ASCII codes <= 32 are skipped. * @tparam SkipWs - skip whitespace characters at the beginning of the line. All characters with ASCII codes <= 32 are skipped.
* @tparam AllowSign - whether the '+' sign is allowed before a number. * @tparam AllowSign - whether the '+' sign is allowed before a number.
* @return std::tuple<T, IntConvertResult, size_t> - a tuple of the received number, the success of the conversion and the number of characters processed. * @return convert_result<T> - a tuple of the received number, the success of the conversion and the number of characters processed.
*/ */
template<ToIntNumber T, bool CheckOverflow = true, unsigned Base = 0, bool SkipWs = true, bool AllowSign = true> template<ToIntNumber T, bool CheckOverflow = true, unsigned Base = 0, bool SkipWs = true, bool AllowSign = true>
std::tuple<T, IntConvertResult, size_t> to_int() const noexcept { convert_result<T> to_int() const noexcept {
return int_convert::to_integer<K, T, Base, CheckOverflow, SkipWs, AllowSign>(_str(), _len()); return int_convert::to_integer<K, T, Base, CheckOverflow, SkipWs, AllowSign>(_str(), _len());
} }
/*! /*!
@ -1464,7 +1471,7 @@ public:
* @en @brief Convert string to double. * @en @brief Convert string to double.
* @return std::optional<double>. * @return std::optional<double>.
*/ */
template<bool SkipWS = true> template<bool SkipWS = true, bool AllowPlus = true>
std::optional<double> to_double() const noexcept { std::optional<double> to_double() const noexcept {
size_t len = _len(); size_t len = _len();
const K* ptr = _str(); const K* ptr = _str();
@ -1474,15 +1481,30 @@ public:
ptr++; ptr++;
} }
} }
if (len) { if constexpr (AllowPlus) {
return impl_to_double(ptr, ptr + len); if (len && *ptr == K('+')) {
ptr++;
len--;
}
} }
return {}; if (!len) {
return {};
}
#ifdef __linux__
if constexpr(sizeof(K) == 1) {
double d{};
if (std::from_chars(ptr, ptr + len, d).ec == std::errc{}) {
return d;
}
return {};
}
#endif
return impl_to_double(ptr, ptr + len);
} }
/*! /*!
* @ru @brief Преобразовать строку в double. * @ru @brief Преобразовать строку в 16ричной записи в double. Пока работает только для char.
* @return std::optional<double>. * @return std::optional<double>.
* @en @brief Convert string to double. * @en @brief Convert string in hex form to double.
* @return std::optional<double>. * @return std::optional<double>.
*/ */
template<bool SkipWS = true> requires (sizeof(K) == 1) template<bool SkipWS = true> requires (sizeof(K) == 1)

View File

@ -1,11 +1,11 @@
# simstr - String object and function library # simstr - String object and function library
[![CMake on multiple platforms](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml/badge.svg)](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml) [![CMake on multiple platforms](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml/badge.svg)](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml)
Version 1.2.5. Version 1.2.6.
<span class="obfuscator"><a href="readme_ru.md">On Russian | По-русски</a></span> <span class="obfuscator"><a href="readme_ru.md">On Russian | По-русски</a></span>
This library contains the implementation of several types of string objects and various algorithms for working with strings. This library contains the modern implementation of several types of string objects and various algorithms for working with strings.
The goal of the library is to make working with strings in C++ as simple and easy as in many other languages, especially The goal of the library is to make working with strings in C++ as simple and easy as in many other languages, especially
scripting languages, while maintaining optimality and performance at the level of C and C++, and even improving them. scripting languages, while maintaining optimality and performance at the level of C and C++, and even improving them.
@ -47,8 +47,7 @@ then the simstr approach will also be clear to you.
- Parsing integers with the possibility of "fine" tuning during compilation - you can set options for checking overflow, - Parsing integers with the possibility of "fine" tuning during compilation - you can set options for checking overflow,
skipping whitespace characters, a specific radix or auto-selection by prefixes `0x`, `0`, `0b`, `0o`, skipping whitespace characters, a specific radix or auto-selection by prefixes `0x`, `0`, `0b`, `0o`,
admissibility of the `+` sign. Parsing is implemented for all types of strings and characters. admissibility of the `+` sign. Parsing is implemented for all types of strings and characters.
- Parsing double is currently implemented by calling the standard library and only works for `char`, `wchar_t` strings and types compatible with - Parsing doubles for all types of characters.
`wchar_t` in size.
- Minimal Unicode support is included when converting `upper`, `lower` and case-insensitive string comparison. - Minimal Unicode support is included when converting `upper`, `lower` and case-insensitive string comparison.
It only works for characters in the first plane of Unicode (up to 0xFFFF), and when changing case, it does not take into account cases where one code point It only works for characters in the first plane of Unicode (up to 0xFFFF), and when changing case, it does not take into account cases where one code point
can be converted into several, that is, the case conversion of characters corresponds to `std::towupper`, `std::towlower` for the unicode locale, only faster and can work with any type of characters. can be converted into several, that is, the case conversion of characters corresponds to `std::towupper`, `std::towlower` for the unicode locale, only faster and can work with any type of characters.

View File

@ -1,11 +1,11 @@
# simstr - библиотека строковых объектов и функций # simstr - библиотека строковых объектов и функций
[![CMake on multiple platforms](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml/badge.svg)](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml) [![CMake on multiple platforms](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml/badge.svg)](https://github.com/orefkov/simstr/actions/workflows/cmake-multi-platform.yml)
Версия 1.2.5. Версия 1.2.6.
<span class="obfuscator"><a href="readme.md">On English | По-английски</a></span> <span class="obfuscator"><a href="readme.md">On English | По-английски</a></span>
В этой библиотеке содержится реализация нескольких видов строковых объектов и различных алгоритмов для работы со строками. В этой библиотеке содержится современная реализация нескольких видов строковых объектов и различных алгоритмов для работы со строками.
Цель библиотеки - сделать работу со строками в С++ такой же простой и лёгкой, как во множестве других языков, особенно Цель библиотеки - сделать работу со строками в С++ такой же простой и лёгкой, как во множестве других языков, особенно
скриптовых, но при этом сохранив оптимальность и производительность на уровне С и C++, и даже улучшив их. скриптовых, но при этом сохранив оптимальность и производительность на уровне С и C++, и даже улучшив их.
@ -47,8 +47,7 @@
- Парсинг целых чисел с возможностью "тонкой" настройки при компиляции - можно задавать опции проверки переполнения, - Парсинг целых чисел с возможностью "тонкой" настройки при компиляции - можно задавать опции проверки переполнения,
пропуск пробельных символов, конкретное основание счисления либо автовыбор по префиксам `0x`, `0`, `0b`, `0o`, пропуск пробельных символов, конкретное основание счисления либо автовыбор по префиксам `0x`, `0`, `0b`, `0o`,
допустимость знака `+`. Парсинг реализован для всех видов строк и символов. допустимость знака `+`. Парсинг реализован для всех видов строк и символов.
- Парсинг double пока реализован вызовом стандартной библиотеки и работает только для строк `char`, `wchar_t` и совместимых с - Парсинг double для всех типов символов.
`wchar_t` по размеру типов.
- Содержится минимальная поддержка Unicode при преобразовании `upper`, `lower` и регистро-независимом сравнении строк. - Содержится минимальная поддержка Unicode при преобразовании `upper`, `lower` и регистро-независимом сравнении строк.
Работает только для символов первой плоскости Unicode (до 0xFFFF), а при смене регистра не учитываются случаи, когда один code point Работает только для символов первой плоскости Unicode (до 0xFFFF), а при смене регистра не учитываются случаи, когда один code point
может преобразовываться в несколько, то есть преобразование регистра символов соответствует `std::towupper`, `std::towlower` для unicode локали, только быстрее и может работать с любым видом символов. может преобразовываться в несколько, то есть преобразование регистра символов соответствует `std::towupper`, `std::towlower` для unicode локали, только быстрее и может работать с любым видом символов.

View File

@ -544,6 +544,7 @@ SIMSTR_API size_t unicode_traits<u32s>::hashiu(const u32s* src, size_t l) {
} }
return h; return h;
} }
namespace { namespace {
// Based on simdjson sources https://github.com/simdjson/simdjson/blob/667d0ed3c77f55cbda2082b034168d69898d1f88/include/simdjson/compile_time_json-inl.h#L189 // Based on simdjson sources https://github.com/simdjson/simdjson/blob/667d0ed3c77f55cbda2082b034168d69898d1f88/include/simdjson/compile_time_json-inl.h#L189
@ -1385,18 +1386,22 @@ SIMSTR_API std::optional<double> impl_to_double(const K* start, const K* end) {
return *pointer; return *pointer;
}; };
const K* srcinit = start; const K* srcinit = start;
bool negative = false; bool negative = (get_value(start) == '-');
if (get_value(start) == '+') { start += (uint8_t)negative;
start++; if (end - start == 3) {
} else { if (!negative && (start[0] | 0x20) == K('n')) {
negative = (get_value(start) == '-'); if ((start[1] | 0x20) == K('a') && (start[2] | 0x20) == K('n')) {
return std::nan("0");
}
} else if ((start[0] | 0x20) == K('i') && (start[1] | 0x20) == K('n') && (start[2] | 0x20) == K('f')) {
return negative ? -std::numeric_limits<double>::infinity() : std::numeric_limits<double>::infinity();
}
} }
start += uint8_t(negative); uint64_t digits = 0;
uint64_t i = 0;
const K* p = start; const K* p = start;
p += parse_digit(get_value(p), i); p += parse_digit(get_value(p), digits);
bool leading_zero = (i == 0); bool leading_zero = (digits == 0);
while (parse_digit(get_value(p), i)) { while (parse_digit(get_value(p), digits)) {
p++; p++;
} }
if (p == start) { if (p == start) {
@ -1411,11 +1416,11 @@ SIMSTR_API std::optional<double> impl_to_double(const K* start, const K* end) {
if (get_value(p) == '.') { if (get_value(p) == '.') {
p++; p++;
const K* start_decimal_digits = p; const K* start_decimal_digits = p;
if (!parse_digit(get_value(p), i)) { if (!parse_digit(get_value(p), digits)) {
return {}; return {};
} // no decimal digits } // no decimal digits
p++; p++;
while (parse_digit(get_value(p), i)) { while (parse_digit(get_value(p), digits)) {
p++; p++;
} }
exponent = -(p - start_decimal_digits); exponent = -(p - start_decimal_digits);
@ -1448,11 +1453,11 @@ SIMSTR_API std::optional<double> impl_to_double(const K* start, const K* end) {
exponent += exp_neg ? 0 - exp : exp; exponent += exp_neg ? 0 - exp : exp;
} }
if (exponent < smallest_power || exponent > largest_power) { if (exponent < smallest_power || exponent > largest_power || p != end) {
return {}; return {};
} }
double d; double d;
if (!compute_float_64(exponent, i, negative, d)) { if (!compute_float_64(exponent, digits, negative, d)) {
return {}; return {};
} }
return d; return d;