10#include "Stroika/Foundation/Containers/Common.h"
11#include "Stroika/Foundation/Math/Common.h"
12#include "Stroika/Foundation/Memory/Common.h"
24 : fSignificantFigures_{p}
27 constexpr SignificantFigures::SignificantFigures (FullFlag)
28 : fSignificantFigures_{}
31 template <
floating_po
int T>
32 constexpr auto SignificantFigures::GetEffectiveSignificantFigures () const -> RepType
34 return fSignificantFigures_.value_or (numeric_limits<T>::max_digits10);
36 template <IStdBasicStringCompatibleCharacter CHAR>
37 constexpr auto SignificantFigures::Calculate (span<const CHAR> number) -> RepType
40 bool dotPresent =
false;
41 bool scientific =
false;
42 for (
const CHAR c : number) {
56 RepType trailingZeros{0};
57 bool eatLeadingZeros = not scientific;
58 bool gotoDone =
false;
59 for (
const CHAR c : number) {
65 if (eatLeadingZeros) {
82 eatLeadingZeros =
false;
101 if (eatLeadingZeros) {
130 constexpr inline SignificantFigures SignificantFigures::kFullPrecision{SignificantFigures::FullFlag::eFull};
137 inline ToStringOptions::ToStringOptions (
const locale& l)
146 : fFmtFlags_{fmtFlags}
150 : fSignificantFigures_{precision}
154 : fFloatFormat_{scientificNotation}
157 DISABLE_COMPILER_GCC_WARNING_START (
"GCC diagnostic ignored \"-Wdeprecated-declarations\"");
158 DISABLE_COMPILER_CLANG_WARNING_START (
"clang diagnostic ignored \"-Wdeprecated-declarations\"");
161 : fTrimTrailingZeros_{trimTrailingZeros == TrimTrailingZerosType::eTrimZeros}
164 DISABLE_COMPILER_MSC_WARNING_END (4996)
165 DISABLE_COMPILER_CLANG_WARNING_END ("clang diagnostic ignored \"-Wdeprecated-declarations\"");
166 DISABLE_COMPILER_GCC_WARNING_END ("GCC diagnostic ignored \"-Wdeprecated-declarations\"");
168 constexpr ToStringOptions::ToStringOptions (const ToStringOptions& b1, const ToStringOptions& b2)
169 : ToStringOptions{b1}
171 Memory::CopyToIf (&fSignificantFigures_, b2.fSignificantFigures_);
172 Memory::CopyToIf (&fFmtFlags_, b2.fFmtFlags_);
173 fUseCurrentLocale_ = b2.fUseCurrentLocale_;
174 Memory::CopyToIf (&fUseLocale_, b2.fUseLocale_);
175 Memory::CopyToIf (&fTrimTrailingZeros_, b2.fTrimTrailingZeros_);
176 Memory::CopyToIf (&fFloatFormat_, b2.fFloatFormat_);
178 template <
typename... ARGS>
180 : ToStringOptions{ToStringOptions{b1, b2}, forward<ARGS> (args)...}
183 constexpr optional<SignificantFigures> ToStringOptions::GetSignificantFigures ()
const
185 return fSignificantFigures_;
187 constexpr optional<bool> ToStringOptions::GetTrimTrailingZeros ()
const
189 return fTrimTrailingZeros_;
193 if (fUseCurrentLocale_) {
199 static const locale kCLocale_ = locale::classic ();
204 if (not fUseLocale_ and not fUseCurrentLocale_) {
209 constexpr optional<FloatFormatType> ToStringOptions::GetFloatFormat ()
const
211 return fFloatFormat_;
213 constexpr optional<ios_base::fmtflags> ToStringOptions::GetIOSFmtFlags ()
const
219 inline void TrimTrailingZeros_NotSci_ (String* strResult)
225 Require (not strResult->Find (
'e', eCaseInsensitive).has_value ());
226 size_t pastDot = strResult->find (
'.');
229 size_t len = strResult->length ();
230 size_t pPastLastZero = len;
231 for (; (pPastLastZero - 1) > pastDot; --pPastLastZero) {
232 if ((*strResult)[pPastLastZero - 1] !=
'0') {
236 if (pPastLastZero == pastDot + 1 and (*strResult)[pPastLastZero - 1] ==
'0') {
239 if (len != pPastLastZero) [[unlikely]] {
240 *strResult = strResult->SubString (0, pPastLastZero);
244 inline void TrimTrailingZeros_MaybeSci_ (String* strResult)
250 if (optional<size_t> whereIsE = strResult->Find (
'e', eCaseInsensitive)) {
251 size_t pastDot = strResult->find (
'.');
254 size_t len = *whereIsE - 1;
255 size_t pPastLastZero = len;
256 for (; (pPastLastZero - 1) > pastDot; --pPastLastZero) {
257 if ((*strResult)[pPastLastZero - 1] !=
'0') {
261 if (len != pPastLastZero) [[unlikely]] {
262 *strResult = strResult->SubString (0, pPastLastZero) + strResult->SubString (*whereIsE);
267 size_t pastDot = strResult->find (
'.');
270 size_t len = strResult->length ();
271 size_t pPastLastZero = len;
272 for (; (pPastLastZero - 1) > pastDot; --pPastLastZero) {
273 if ((*strResult)[pPastLastZero - 1] !=
'0') {
277 if (pPastLastZero == pastDot + 1 and (*strResult)[pPastLastZero - 1] ==
'0') {
280 if (len != pPastLastZero) [[unlikely]] {
281 *strResult = strResult->SubString (0, pPastLastZero);
290 template <
typename T>
291 T CStr2FloatType_ (
const wchar_t* s,
wchar_t** e);
292 template <
typename T>
293 T CStr2FloatType_ (
const char* s,
char** e);
295 inline float CStr2FloatType_ (
const wchar_t* s,
wchar_t** e)
297 return ::wcstof (s, e);
300 inline double CStr2FloatType_ (
const wchar_t* s,
wchar_t** e)
302 return wcstod (s, e);
305 inline long double CStr2FloatType_ (
const wchar_t* s,
wchar_t** e)
307 return wcstold (s, e);
310 inline float CStr2FloatType_ (
const char* s,
char** e)
312 return ::strtof (s, e);
315 inline double CStr2FloatType_ (
const char* s,
char** e)
317 return strtod (s, e);
320 inline long double CStr2FloatType_ (
const char* s,
char** e)
322 return strtold (s, e);
327 template <
typename RETURN_TYPE>
328 RETURN_TYPE ToFloat_ViaStrToD_ (
const char* start,
const char* end,
const char** remainder)
330 Require (start <= end);
332 if (remainder !=
nullptr) {
335 return Math::nan<RETURN_TYPE> ();
339 const char* cst = start;
341 if (remainder ==
nullptr) {
343 if (::isspace (*cst)) {
344 return Math::nan<RETURN_TYPE> ();
348 Memory::StackBuffer<char> tmp;
351 size_t len = end - start;
352 tmp.GrowToSize (len + 1);
353 (void)::memcpy (tmp.begin (), start, len);
357 RETURN_TYPE d = CStr2FloatType_<RETURN_TYPE> (cst, &e);
360 if (remainder ==
nullptr) {
362 d = Math::nan<RETURN_TYPE> ();
366 *remainder = e - cst + start;
371 template <
typename RETURN_TYPE>
372 RETURN_TYPE ToFloat_ViaStrToD_ (
const wchar_t* start,
const wchar_t* end,
const wchar_t** remainder)
374 Require (start <= end);
376 if (remainder !=
nullptr) {
379 return Math::nan<RETURN_TYPE> ();
382 wchar_t* e =
nullptr;
383 const wchar_t* cst = start;
385 if (remainder ==
nullptr) {
387 if (::iswspace (*cst)) {
388 return Math::nan<RETURN_TYPE> ();
392 Memory::StackBuffer<wchar_t> tmp;
395 size_t len = end - start;
396 tmp.GrowToSize (len + 1);
397 (void)::memcpy (tmp.begin (), start, len *
sizeof (wchar_t));
401 RETURN_TYPE d = CStr2FloatType_<RETURN_TYPE> (cst, &e);
404 if (remainder ==
nullptr) {
406 d = Math::nan<RETURN_TYPE> ();
410 *remainder = e - cst + start;
418#if qStroika_Foundation_Debug_AssertionsChecked || not defined(__cpp_lib_to_chars)
419 inline size_t CalcSignificantFigures_ (
const String& numStr,
bool countZerosAtEndAfterDecPoint =
false)
422 bool ignoreRest =
false;
423 bool seenDot =
false;
425 size_t nTrailingZeros{};
426 numStr.Apply ([&] (Character c) {
433 if (c ==
'+' or c ==
'-' or c ==
'.') {
436 if (leading and c ==
'0') {
444 if (countZerosAtEndAfterDecPoint and seenDot) {
454 return countZerosAtEndAfterDecPoint ? n : n - nTrailingZeros;
457 template <
floating_po
int FLOAT_TYPE>
458 String ToString_OptimizedForCLocaleAndNoStreamFlags_ (FLOAT_TYPE f, SignificantFigures precision)
460 using namespace Memory;
461 size_t sz = numeric_limits<FLOAT_TYPE>::max_digits10 + numeric_limits<FLOAT_TYPE>::max_exponent10 + 5;
462 StackBuffer<char> buf{Memory::eUninitialized, sz};
463 ptrdiff_t resultStrLen;
464 unsigned int effectivePrecision = precision.GetEffectiveSignificantFigures<FLOAT_TYPE> ();
467#if defined(__cpp_lib_to_chars)
470 resultStrLen = to_chars (buf.begin (), buf.end (), f, chars_format::general).ptr - buf.
begin ();
473 resultStrLen = to_chars (buf.begin (), buf.end (), f, chars_format::general, effectivePrecision).ptr - buf.begin ();
476 auto mkFmtWithPrecisionArg_ = [] (
char* formatBufferStart, [[maybe_unused]]
char* formatBufferEnd,
char _Spec,
bool forceScientific) ->
char* {
477 char* fmtPtr = formatBufferStart;
485 *fmtPtr++ = forceScientific ?
'e' :
'g';
487 Require (fmtPtr < formatBufferEnd);
488 return formatBufferStart;
491 FLOAT_TYPE useRoundedFloat = Math::Round<FLOAT_TYPE> (f, effectivePrecision);
496 bool forceScientific = fabs (f) >= std::pow (10, effectivePrecision);
498 resultStrLen = ::snprintf (buf.data (), buf.size (),
499 mkFmtWithPrecisionArg_ (std::begin (format), std::end (format),
500 same_as<FLOAT_TYPE, long double> ?
'L' :
'\0', forceScientific),
501 (int)effectivePrecision + 1, f);
502 auto actualPrecIncZeros = CalcSignificantFigures_ (String{span{buf.data (),
static_cast<size_t> (resultStrLen)}},
true);
503 if (actualPrecIncZeros > effectivePrecision) {
504 ptrdiff_t nBytes =
static_cast<ptrdiff_t
> (actualPrecIncZeros) -
static_cast<ptrdiff_t
> (effectivePrecision);
505 auto numberEnd = buf.data () + resultStrLen;
506 auto ePtr = std::find (buf.data (), numberEnd,
'e');
507 if (ePtr != numberEnd) {
508 Assert (buf.data () <= ePtr - nBytes);
509 memmove (ePtr - nBytes, ePtr, (buf.data () + resultStrLen - ePtr));
511 resultStrLen -= nBytes;
517 Verify (resultStrLen > 0 and resultStrLen <
static_cast<int> (sz));
518#if qStroika_Foundation_Debug_AssertionsChecked
520 CalcSignificantFigures_ (String{span{buf.data (),
static_cast<size_t> (resultStrLen)}}) <= effectivePrecision);
522 return String{span{buf.data (),
static_cast<size_t> (resultStrLen)}};
528 template <
floating_po
int T>
529 inline string formatFixed_ (T val,
unsigned int precision)
531#if qCompilerAndStdLib_format_dynamic_spec_Buggy
532 return Common::StdCompat::vformat (
"{:.{}f}", Common::StdCompat::make_format_args (val, precision));
534 return Common::StdCompat::format (
"{:.{}f}", val, precision);
537 template <
floating_po
int T>
538 inline wstring formatFixed_ (
const locale& l, T val,
unsigned int precision)
540#if qCompilerAndStdLib_format_dynamic_spec_Buggy
541 return Common::StdCompat::vformat (l, L
"{:.{}f}", Common::StdCompat::make_wformat_args (val, precision));
543 return Common::StdCompat::format (l, L
"{:.{}f}", val, precision);
546 template <
floating_po
int T>
547 inline String formatNonScientific_ (T val,
unsigned int nSignificantFigures)
549 auto compute = [&] () {
552 return String{formatFixed_ (0.0, nSignificantFigures)};
556 int digits_before =
static_cast<int> (floor (log10 (abs (val)))) + 1;
559 unsigned int precision =
static_cast<unsigned int> (max (0,
static_cast<int> (nSignificantFigures) - digits_before));
561 return String{formatFixed_ (val, precision)};
564 String r = compute ();
572 template <
floating_po
int T>
573 inline String formatNonScientific_ (
const locale& l, T val,
unsigned int nSignificantFigures)
575 auto compute = [&] () {
578 return formatFixed_ (l, 0.0, nSignificantFigures);
582 int digits_before =
static_cast<int> (floor (log10 (abs (val)))) + 1;
585 unsigned int precision =
static_cast<unsigned int> (max (0,
static_cast<int> (nSignificantFigures) - digits_before));
587 return formatFixed_ (l, val, precision);
590 String r = compute ();
597 template <
floating_po
int FLOAT_TYPE>
598 String ToString_GeneralCase_ (FLOAT_TYPE f,
const ToStringOptions& options)
600 unsigned int usePrecision =
601 options.GetSignificantFigures ().value_or (SignificantFigures{}).GetEffectiveSignificantFigures<FLOAT_TYPE> ();
607 Assert (options.GetIOSFmtFlags () == nullopt);
609 if (options.GetUsingLocaleClassic ()) {
610 return formatNonScientific_ (f, usePrecision);
613 return formatNonScientific_ (options.GetUseLocale (), f, usePrecision);
619 static thread_local stringstream s;
620 static const ios_base::fmtflags kDefaultIOSFmtFlags_ = s.flags ();
625 s.imbue (options.GetUseLocale ());
628 s.flags (options.GetIOSFmtFlags ().value_or (kDefaultIOSFmtFlags_));
631 switch (usingFormat) {
634 s.setf (ios_base::scientific, ios_base::floatfield);
635 Assert (usePrecision >= 2);
636 s.precision (usePrecision - 1);
639 s.unsetf (ios_base::floatfield);
645 s.precision (usePrecision);
649 s.setf (ios_base::fixed, ios_base::floatfield);
650 s.precision (usePrecision);
655 DISABLE_COMPILER_GCC_WARNING_START (
"GCC diagnostic ignored \"-Wdeprecated-declarations\"");
656 DISABLE_COMPILER_CLANG_WARNING_START (
"clang diagnostic ignored \"-Wdeprecated-declarations\"");
659 s.precision (usePrecision);
660 bool useScientificNotation = abs (f) >= pow (10, usePrecision / 2) or
661 (f != 0 and abs (f) < pow (10, -static_cast<
int> (usePrecision) / 2));
662 if (useScientificNotation) {
663 s.setf (ios_base::scientific, ios_base::floatfield);
666 s.unsetf (ios_base::floatfield);
667 s.precision (usePrecision);
670 DISABLE_COMPILER_MSC_WARNING_END (4996)
671 DISABLE_COMPILER_CLANG_WARNING_END ("clang diagnostic ignored \"-Wdeprecated-declarations\"");
672 DISABLE_COMPILER_GCC_WARNING_END ("GCC diagnostic ignored \"-Wdeprecated-declarations\"");
680 string ss = s.str ();
687 template <
floating_po
int FLOAT_TYPE>
688 String ToString_String_Implementation_ (FLOAT_TYPE f,
const ToStringOptions& options)
690 auto floatFormat = options.GetFloatFormat ().value_or (FloatFormatType::eDEFAULT);
692 (options.GetUsingLocaleClassic () and not options.GetIOSFmtFlags () and floatFormat == FloatFormatType::eDefaultFloat)
693 ? Private_::ToString_OptimizedForCLocaleAndNoStreamFlags_ (f, options.GetSignificantFigures ().value_or (SignificantFigures{}))
694 : Private_::ToString_GeneralCase_ (f, options);
695 if (floatFormat == FloatFormatType::eFixedPointWithWhitespaceTrimmed or floatFormat == FloatFormatType::eStandard) {
696 TrimTrailingZeros_NotSci_ (&result);
698 if (floatFormat == FloatFormatType::eScientificWithWhitespaceTrimmed) {
699 TrimTrailingZeros_MaybeSci_ (&result);
701#if qCompilerAndStdLib_float2string_defaultfmt_scientificNotStripped_Buggy
702 if (floatFormat == FloatFormatType::eDefaultFloat) {
703 TrimTrailingZeros_MaybeSci_ (&result);
722 template <
typename T, IUNICODECanUnambiguouslyConvertFrom CHAR_T>
723 inline T ToFloat_RespectingLocale_ (
const span<const CHAR_T> srcSpan,
typename span<const CHAR_T>::iterator* remainder)
725 if (srcSpan.empty ()) {
726 if (remainder !=
nullptr) {
727 *remainder = srcSpan.begin ();
729 return Math::nan<T> ();
735 Memory::StackBuffer<CHAR_T> srcBufWithNul{Memory::eUninitialized, srcSpan.size () + 1};
736 Memory::CopyBytes (srcSpan, span{srcBufWithNul});
737 srcBufWithNul[srcSpan.size ()] =
'\0';
738 const CHAR_T* si = srcBufWithNul.begin ();
739 const CHAR_T* ei = srcBufWithNul.end () - 1;
740 const CHAR_T* ri = ei;
743 if (remainder ==
nullptr) {
745 if constexpr (
sizeof (CHAR_T) == 1) {
746 isSpace = std::isspace (*si);
748 else if constexpr (
sizeof (CHAR_T) ==
sizeof (
wchar_t)) {
749 isSpace = std::iswspace (*si);
752 isSpace = std::iswspace (
static_cast<wint_t
> (*si));
755 if (remainder !=
nullptr) {
756 *remainder = srcSpan.begin ();
758 return Math::nan<T> ();
762 static_assert (same_as<T, float> or same_as<T, double> or same_as<T, long double>);
763 if constexpr (
sizeof (CHAR_T) == 1) {
764 if constexpr (same_as<T, float>) {
765 d = ::strtof (
reinterpret_cast<const char*
> (si),
const_cast<char**
> (
reinterpret_cast<const char**
> (&ri)));
767 else if constexpr (same_as<T, double>) {
768 d = ::strtod (
reinterpret_cast<const char*
> (si),
const_cast<char**
> (
reinterpret_cast<const char**
> (&ri)));
770 else if constexpr (same_as<T, long double>) {
771 d = ::strtold (
reinterpret_cast<const char*
> (si),
const_cast<char**
> (
reinterpret_cast<const char**
> (&ri)));
777 else if constexpr (
sizeof (CHAR_T) ==
sizeof (
wchar_t)) {
778 if constexpr (same_as<T, float>) {
779 d = ::wcstof (
reinterpret_cast<const wchar_t*
> (si),
const_cast<wchar_t**
> (
reinterpret_cast<const wchar_t**
> (&ri)));
781 else if constexpr (same_as<T, double>) {
782 d = ::wcstod (
reinterpret_cast<const wchar_t*
> (si),
const_cast<wchar_t**
> (
reinterpret_cast<const wchar_t**
> (&ri)));
784 else if constexpr (same_as<T, long double>) {
785 d = ::wcstold (
reinterpret_cast<const wchar_t*
> (si),
const_cast<wchar_t**
> (
reinterpret_cast<const wchar_t**
> (&ri)));
793 Memory::StackBuffer<wchar_t> wideBuf{Memory::eUninitialized, UTFConvert::ComputeTargetBufferSize<wchar_t> (srcSpan)};
794 span<const wchar_t> wideSpan = UTFConvert::kThe.ConvertSpan (srcSpan, span{wideBuf});
795 if (remainder ==
nullptr) {
796 d = ToFloat_RespectingLocale_<T, wchar_t> (wideSpan,
nullptr);
800 span<const wchar_t>::iterator wideRemainder;
801 d = ToFloat_RespectingLocale_<T> (wideSpan, &wideRemainder);
802 ri = UTFConvert::kThe.ConvertOffset<CHAR_T> (wideSpan, wideRemainder - wideSpan.begin ()) + si;
805 if (remainder ==
nullptr) {
812 *remainder = srcSpan.begin () + (ri - si);
824 template <Common::IAnyOf<String,
string, w
string> STRING_TYPE,
floating_po
int FLOAT_TYPE>
827 if constexpr (same_as<STRING_TYPE, String>) {
828 return Private_::ToString_String_Implementation_ (f, options);
830 else if constexpr (same_as<STRING_TYPE, string>) {
833 return Private_::ToString_String_Implementation_ (f, options).AsASCII ();
835 else if constexpr (same_as<STRING_TYPE, wstring>) {
838 return Private_::ToString_String_Implementation_ (f, options).template As<wstring> ();
847 template <
floating_po
int T, IUNICODECanUnambiguouslyConvertFrom CHAR_T>
850 if constexpr (same_as<remove_cv_t<CHAR_T>,
char>) {
860#if defined(__cpp_lib_to_chars) and not qCompilerAndStdLib_from_chars_and_tochars_FP_Precision_Buggy
861#if qCompilerAndStdLib_from_chars_reads_past_end_Buggy
864 auto b = asciiS.begin ();
865 auto e = asciiS.end ();
866#if qCompilerAndStdLib_from_chars_reads_past_end_Buggy
869 if (b != e and *b ==
'+') [[unlikely]] {
872 auto [ptr, ec] = from_chars (b, e, result);
873 if (ec == errc::result_out_of_range) [[unlikely]] {
874 result = *b ==
'-' ? -numeric_limits<T>::infinity () : numeric_limits<T>::infinity ();
876 else if (ec != std::errc{} or ptr != e) [[unlikely]] {
879 result = Private_::ToFloat_RespectingLocale_<T> (s,
nullptr);
882 result = Private_::ToFloat_RespectingLocale_<T> (span<const char>{asciiS},
nullptr);
886 return Private_::ToFloat_RespectingLocale_<T> (s,
nullptr);
888 template <
floating_po
int T, IUNICODECanUnambiguouslyConvertFrom CHAR_T>
889 T ToFloat (span<const CHAR_T> s,
typename span<const CHAR_T>::iterator* remainder)
892 if constexpr (same_as<remove_cv_t<CHAR_T>,
char>) {
899#if defined(__cpp_lib_to_chars) and not qCompilerAndStdLib_from_chars_and_tochars_FP_Precision_Buggy
903 char* b = asciiS.begin ();
904 char* e = asciiS.end ();
905 if (b != e and *b ==
'+') [[unlikely]] {
908 auto [ptr, ec] = from_chars (b, e, result);
909 if (ec == errc::result_out_of_range) [[unlikely]] {
910 result = *b ==
'-' ? -numeric_limits<T>::infinity () : numeric_limits<T>::infinity ();
912 else if (ec != std::errc{} or ptr != e) [[unlikely]] {
915 typename span<const CHAR_T>::iterator tmpI;
916 result = Private_::ToFloat_RespectingLocale_<T> (s, &tmpI);
917 ptr = tmpI - s.begin () + b;
920 span{
reinterpret_cast<const char8_t*
> (b),
reinterpret_cast<const char8_t*
> (e)}, ptr - b);
921 Assert (*remainder <= s.end ());
925 return Private_::ToFloat_RespectingLocale_<T> (s, remainder);
927 template <
floating_po
int T,
typename STRINGISH_ARG>
928 inline T
ToFloat (STRINGISH_ARG&& s)
929 requires (IConvertibleToString<STRINGISH_ARG> or is_convertible_v<STRINGISH_ARG, std::string>)
931 using DecayedStringishArg = remove_cvref_t<STRINGISH_ARG>;
932 if constexpr (same_as<DecayedStringishArg, const char*> or same_as<DecayedStringishArg, const char8_t*> or
933 same_as<DecayedStringishArg, const char16_t*> or same_as<DecayedStringishArg, const char32_t*> or
934 same_as<DecayedStringishArg, const wchar_t*>) {
935 return ToFloat<T> (span{s, CString::Length (s)});
937 else if constexpr (same_as<DecayedStringishArg, String>) {
938 Memory::StackBuffer<wchar_t> ignored;
939 auto sp = s.template GetData<wchar_t> (&ignored);
940 return ToFloat<T> (sp);
942 else if constexpr (is_convertible_v<DecayedStringishArg, std::string>) {
944 return ToFloat<T> (span{ss.data (), ss.size ()});
947 return ToFloat<T> (String{forward<STRINGISH_ARG> (s)});
950 template <
floating_po
int T>
951 inline T
ToFloat (
const String& s, String* remainder)
954 Memory::StackBuffer<wchar_t> ignored;
955 span<const wchar_t> srcSpan = s.GetData (&ignored);
956 span<const wchar_t>::iterator tmpRemainder;
957 auto result = ToFloat<T> (srcSpan, &tmpRemainder);
958 *remainder = String{srcSpan.subspan (tmpRemainder - srcSpan.begin ())};
964 template <
typename T>
965 [[deprecated (
"Since Stroika v3.0d1 - use span overload")]]
inline T
ToFloat (
const wchar_t* start,
const wchar_t* end,
const wchar_t** remainder)
967 Require (start <= end);
970#if defined(__cpp_lib_to_chars) and not qCompilerAndStdLib_from_chars_and_tochars_FP_Precision_Buggy
975 Memory::StackBuffer<char> asciiS;
977 auto b = asciiS.begin ();
979 auto e = asciiS.end ();
980 if (remainder !=
nullptr) [[unlikely]] {
982 while (b != e and iswspace (*b)) [[unlikely]] {
986 if (b != e and *b ==
'+') [[unlikely]] {
990 auto [ptr, ec] = from_chars (b, e, result);
991 if (ec == errc::result_out_of_range) [[unlikely]] {
992 return *b ==
'-' ? -numeric_limits<T>::infinity () : numeric_limits<T>::infinity ();
995 if (ec != std::errc{}) [[unlikely]] {
996 result = Math::nan<T> ();
1000 if (remainder ==
nullptr) [[likely]] {
1001 if (ptr != e) [[unlikely]] {
1002 result = Math::nan<T> ();
1006 *remainder = ptr - originalB + start;
1010 result = Private_::ToFloat_ViaStrToD_<T> (start, end, remainder);
1013 result = Private_::ToFloat_ViaStrToD_<T> (start, end, remainder);
1017 template <
typename T =
double>
1018 [[deprecated (
"Since Stroika v3.0d1 - use span overload")]]
inline T
ToFloat (
const char* start,
const char* end)
1020 return ToFloat<T> (span{start, end});
1022 template <
typename T =
double>
1023 [[deprecated (
"Since Stroika v3.0d1 - use span overload")]]
inline T
ToFloat (
const wchar_t* start,
const wchar_t* end)
1025 return ToFloat<T> (span{start, end});
#define RequireNotReached()
#define RequireNotNull(p)
#define AssertNotReached()
#define Stroika_ATTRIBUTE_INDETERMINATE
Stroika_ATTRIBUTE_INDETERMINATE is used where you would use a C++ attribute for a variable that is in...
constexpr bool IsASCII() const noexcept
Return true iff the given character (or all in span) is (are) in the ascii range [0....
static bool AsASCIIQuietly(span< const CHAR_T > fromS, RESULT_T *into)
static String FromNarrowString(const char *from, const locale &l)
static constexpr size_t npos
static const UTFConvert kThe
Nearly always use this default UTFConvert.
nonvirtual size_t ConvertOffset(span< const SRC_T > source, size_t srcIndex) const
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
nonvirtual void push_back(Common::ArgByValueType< T > e)
nonvirtual Iterator< T > begin() const
Support for ranged for, and STL syntax in general.
T ToFloat(span< const CHAR_T > s)
@ eScientificWithWhitespaceTrimmed
@ eFixedPoint
eFixedPointWithoutWhitespaceTrimmed
@ eFixedPointWithWhitespaceTrimmed
@ eScientific
eScientificWithoutWhitespaceTrimmed
@ eStandard
Somewhat like defaultfloat, but never uses scientific notation.
STRING_TYPE ToString(FLOAT_TYPE f, const ToStringOptions &options={})
DISABLE_COMPILER_MSC_WARNING_START(4996)
constexpr ToStringOptions()=default
nonvirtual locale GetUseLocale() const
return the selected locale object
nonvirtual bool GetUsingLocaleClassic() const
return true if locale used is locale::classic() - the 'C' locale; mostly used as optimization/special...