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 DISABLE_COMPILER_GCC_WARNING_START (
"GCC diagnostic ignored \"-Wrestrict\"");
398 DISABLE_COMPILER_GCC_WARNING_START (
"GCC diagnostic ignored \"-Wstringop-overflow=\"");
399 DISABLE_COMPILER_GCC_WARNING_START (
"GCC diagnostic ignored \"-Wstringop-overflow\"");
400 (void)::memcpy (tmp.begin (), start, len *
sizeof (wchar_t));
401 DISABLE_COMPILER_GCC_WARNING_END (
"GCC diagnostic ignored \"-Wstringop-overflow\"");
402 DISABLE_COMPILER_GCC_WARNING_END (
"GCC diagnostic ignored \"-Wstringop-overflow=\"");
403 DISABLE_COMPILER_GCC_WARNING_END (
"GCC diagnostic ignored \"-Wrestrict\"");
407 RETURN_TYPE d = CStr2FloatType_<RETURN_TYPE> (cst, &e);
410 if (remainder ==
nullptr) {
412 d = Math::nan<RETURN_TYPE> ();
416 *remainder = e - cst + start;
424#if qStroika_Foundation_Debug_AssertionsChecked || !(__cpp_lib_to_chars >= 201611)
425 inline size_t CalcSignificantFigures_ (
const String& numStr,
bool countZerosAtEndAfterDecPoint =
false)
428 bool ignoreRest =
false;
429 bool seenDot =
false;
431 size_t nTrailingZeros{};
432 numStr.Apply ([&] (Character c) {
439 if (c ==
'+' or c ==
'-' or c ==
'.') {
442 if (leading and c ==
'0') {
450 if (countZerosAtEndAfterDecPoint and seenDot) {
460 return countZerosAtEndAfterDecPoint ? n : n - nTrailingZeros;
463 template <
floating_po
int FLOAT_TYPE>
464 String ToString_OptimizedForCLocaleAndNoStreamFlags_ (FLOAT_TYPE f, SignificantFigures precision)
466 using namespace Memory;
467 size_t sz = numeric_limits<FLOAT_TYPE>::max_digits10 + numeric_limits<FLOAT_TYPE>::max_exponent10 + 5;
468 StackBuffer<char> buf{Memory::eUninitialized, sz};
469 ptrdiff_t resultStrLen;
470 unsigned int effectivePrecision = precision.GetEffectiveSignificantFigures<FLOAT_TYPE> ();
473#if __cpp_lib_to_chars >= 201611
476 resultStrLen = to_chars (buf.begin (), buf.end (), f, chars_format::general).ptr - buf.
begin ();
479 resultStrLen = to_chars (buf.begin (), buf.end (), f, chars_format::general, effectivePrecision).ptr - buf.begin ();
482 auto mkFmtWithPrecisionArg_ = [] (
char* formatBufferStart, [[maybe_unused]]
char* formatBufferEnd,
char _Spec,
bool forceScientific) ->
char* {
483 char* fmtPtr = formatBufferStart;
491 *fmtPtr++ = forceScientific ?
'e' :
'g';
493 Require (fmtPtr < formatBufferEnd);
494 return formatBufferStart;
497 FLOAT_TYPE useRoundedFloat = Math::Round<FLOAT_TYPE> (f, effectivePrecision);
502 bool forceScientific = fabs (f) >= std::pow (10, effectivePrecision);
504 resultStrLen = ::snprintf (buf.data (), buf.size (),
505 mkFmtWithPrecisionArg_ (std::begin (format), std::end (format),
506 same_as<FLOAT_TYPE, long double> ?
'L' :
'\0', forceScientific),
507 (int)effectivePrecision + 1, f);
508 auto actualPrecIncZeros = CalcSignificantFigures_ (String{span{buf.data (),
static_cast<size_t> (resultStrLen)}},
true);
509 if (actualPrecIncZeros > effectivePrecision) {
510 ptrdiff_t nBytes =
static_cast<ptrdiff_t
> (actualPrecIncZeros) -
static_cast<ptrdiff_t
> (effectivePrecision);
511 auto numberEnd = buf.data () + resultStrLen;
512 auto ePtr = std::find (buf.data (), numberEnd,
'e');
513 if (ePtr != numberEnd) {
514 Assert (buf.data () <= ePtr - nBytes);
515 memmove (ePtr - nBytes, ePtr, (buf.data () + resultStrLen - ePtr));
517 resultStrLen -= nBytes;
523 Verify (resultStrLen > 0 and resultStrLen <
static_cast<int> (sz));
524#if qStroika_Foundation_Debug_AssertionsChecked
526 CalcSignificantFigures_ (String{span{buf.data (),
static_cast<size_t> (resultStrLen)}}) <= effectivePrecision);
528 return String{span{buf.data (),
static_cast<size_t> (resultStrLen)}};
533 template <
floating_po
int T>
534 inline String formatNonScientific_ (T val,
unsigned int nSignificantFigures)
536 auto compute = [&] () {
539 return String{Common::StdCompat::format (
"{:.{}f}", 0.0, nSignificantFigures)};
543 int digits_before =
static_cast<int> (floor (log10 (abs (val)))) + 1;
546 unsigned int precision =
static_cast<unsigned int> (max (0,
static_cast<int> (nSignificantFigures) - digits_before));
550 return String{Common::StdCompat::format (
"{:.{}f}", val, precision)};
553 String r = compute ();
561 template <
floating_po
int T>
562 inline String formatNonScientific_ (
const locale& l, T val,
unsigned int nSignificantFigures)
564 auto compute = [&] () {
567 return Common::StdCompat::format (l, L
"{:.{}f}", 0.0, nSignificantFigures);
571 int digits_before =
static_cast<int> (floor (log10 (abs (val)))) + 1;
574 unsigned int precision =
static_cast<unsigned int> (max (0,
static_cast<int> (nSignificantFigures) - digits_before));
578 return Common::StdCompat::format (l, L
"{:.{}f}", val, precision);
581 String r = compute ();
588 template <
floating_po
int FLOAT_TYPE>
589 String ToString_GeneralCase_ (FLOAT_TYPE f,
const ToStringOptions& options)
591 unsigned int usePrecision =
592 options.GetSignificantFigures ().value_or (SignificantFigures{}).GetEffectiveSignificantFigures<FLOAT_TYPE> ();
598 Assert (options.GetIOSFmtFlags () == nullopt);
600 if (options.GetUsingLocaleClassic ()) {
601 return formatNonScientific_ (f, usePrecision);
604 return formatNonScientific_ (options.GetUseLocale (), f, usePrecision);
610 static thread_local stringstream s;
611 static const ios_base::fmtflags kDefaultIOSFmtFlags_ = s.flags ();
616 s.imbue (options.GetUseLocale ());
619 s.flags (options.GetIOSFmtFlags ().value_or (kDefaultIOSFmtFlags_));
622 switch (usingFormat) {
625 s.setf (ios_base::scientific, ios_base::floatfield);
626 Assert (usePrecision >= 2);
627 s.precision (usePrecision - 1);
630 s.unsetf (ios_base::floatfield);
636 s.precision (usePrecision);
640 s.setf (ios_base::fixed, ios_base::floatfield);
641 s.precision (usePrecision);
646 DISABLE_COMPILER_GCC_WARNING_START (
"GCC diagnostic ignored \"-Wdeprecated-declarations\"");
647 DISABLE_COMPILER_CLANG_WARNING_START (
"clang diagnostic ignored \"-Wdeprecated-declarations\"");
650 s.precision (usePrecision);
651 bool useScientificNotation = abs (f) >= pow (10, usePrecision / 2) or
652 (f != 0 and abs (f) < pow (10, -static_cast<
int> (usePrecision) / 2));
653 if (useScientificNotation) {
654 s.setf (ios_base::scientific, ios_base::floatfield);
657 s.unsetf (ios_base::floatfield);
658 s.precision (usePrecision);
661 DISABLE_COMPILER_MSC_WARNING_END (4996)
662 DISABLE_COMPILER_CLANG_WARNING_END ("clang diagnostic ignored \"-Wdeprecated-declarations\"");
663 DISABLE_COMPILER_GCC_WARNING_END ("GCC diagnostic ignored \"-Wdeprecated-declarations\"");
671 string ss = s.str ();
678 template <
floating_po
int FLOAT_TYPE>
679 String ToString_String_Implementation_ (FLOAT_TYPE f,
const ToStringOptions& options)
681 auto floatFormat = options.GetFloatFormat ().value_or (FloatFormatType::eDEFAULT);
683 (options.GetUsingLocaleClassic () and not options.GetIOSFmtFlags () and floatFormat == FloatFormatType::eDefaultFloat)
684 ? Private_::ToString_OptimizedForCLocaleAndNoStreamFlags_ (f, options.GetSignificantFigures ().value_or (SignificantFigures{}))
685 : Private_::ToString_GeneralCase_ (f, options);
686 if (floatFormat == FloatFormatType::eFixedPointWithWhitespaceTrimmed or floatFormat == FloatFormatType::eStandard) {
687 TrimTrailingZeros_NotSci_ (&result);
689 if (floatFormat == FloatFormatType::eScientificWithWhitespaceTrimmed) {
690 TrimTrailingZeros_MaybeSci_ (&result);
692#if qCompilerAndStdLib_float2string_defaultfmt_scientificNotStripped_Buggy
693 if (floatFormat == FloatFormatType::eDefaultFloat) {
694 TrimTrailingZeros_MaybeSci_ (&result);
713 template <
typename T, IUNICODECanUnambiguouslyConvertFrom CHAR_T>
714 inline T ToFloat_RespectingLocale_ (
const span<const CHAR_T> srcSpan,
typename span<const CHAR_T>::iterator* remainder)
716 if (srcSpan.empty ()) {
717 if (remainder !=
nullptr) {
718 *remainder = srcSpan.begin ();
720 return Math::nan<T> ();
726 Memory::StackBuffer<CHAR_T> srcBufWithNul{Memory::eUninitialized, srcSpan.size () + 1};
727 Memory::CopyBytes (srcSpan, span{srcBufWithNul});
728 srcBufWithNul[srcSpan.size ()] =
'\0';
729 const CHAR_T* si = srcBufWithNul.begin ();
730 const CHAR_T* ei = srcBufWithNul.end () - 1;
731 const CHAR_T* ri = ei;
734 if (remainder ==
nullptr) {
736 if constexpr (
sizeof (CHAR_T) == 1) {
737 isSpace = std::isspace (*si);
739 else if constexpr (
sizeof (CHAR_T) ==
sizeof (
wchar_t)) {
740 isSpace = std::iswspace (*si);
743 isSpace = std::iswspace (
static_cast<wint_t
> (*si));
746 if (remainder !=
nullptr) {
747 *remainder = srcSpan.begin ();
749 return Math::nan<T> ();
753 static_assert (same_as<T, float> or same_as<T, double> or same_as<T, long double>);
754 if constexpr (
sizeof (CHAR_T) == 1) {
755 if constexpr (same_as<T, float>) {
756 d = ::strtof (
reinterpret_cast<const char*
> (si),
const_cast<char**
> (
reinterpret_cast<const char**
> (&ri)));
758 else if constexpr (same_as<T, double>) {
759 d = ::strtod (
reinterpret_cast<const char*
> (si),
const_cast<char**
> (
reinterpret_cast<const char**
> (&ri)));
761 else if constexpr (same_as<T, long double>) {
762 d = ::strtold (
reinterpret_cast<const char*
> (si),
const_cast<char**
> (
reinterpret_cast<const char**
> (&ri)));
768 else if constexpr (
sizeof (CHAR_T) ==
sizeof (
wchar_t)) {
769 if constexpr (same_as<T, float>) {
770 d = ::wcstof (
reinterpret_cast<const wchar_t*
> (si),
const_cast<wchar_t**
> (
reinterpret_cast<const wchar_t**
> (&ri)));
772 else if constexpr (same_as<T, double>) {
773 d = ::wcstod (
reinterpret_cast<const wchar_t*
> (si),
const_cast<wchar_t**
> (
reinterpret_cast<const wchar_t**
> (&ri)));
775 else if constexpr (same_as<T, long double>) {
776 d = ::wcstold (
reinterpret_cast<const wchar_t*
> (si),
const_cast<wchar_t**
> (
reinterpret_cast<const wchar_t**
> (&ri)));
784 Memory::StackBuffer<wchar_t> wideBuf{Memory::eUninitialized, UTFConvert::ComputeTargetBufferSize<wchar_t> (srcSpan)};
785 span<const wchar_t> wideSpan = UTFConvert::kThe.ConvertSpan (srcSpan, span{wideBuf});
786 if (remainder ==
nullptr) {
787 d = ToFloat_RespectingLocale_<T, wchar_t> (wideSpan,
nullptr);
791 span<const wchar_t>::iterator wideRemainder;
792 d = ToFloat_RespectingLocale_<T> (wideSpan, &wideRemainder);
793 ri = UTFConvert::kThe.ConvertOffset<CHAR_T> (wideSpan, wideRemainder - wideSpan.begin ()) + si;
796 if (remainder ==
nullptr) {
803 *remainder = srcSpan.begin () + (ri - si);
815 template <Common::IAnyOf<String,
string, w
string> STRING_TYPE,
floating_po
int FLOAT_TYPE>
818 if constexpr (same_as<STRING_TYPE, String>) {
819 return Private_::ToString_String_Implementation_ (f, options);
821 else if constexpr (same_as<STRING_TYPE, string>) {
824 return Private_::ToString_String_Implementation_ (f, options).AsASCII ();
826 else if constexpr (same_as<STRING_TYPE, wstring>) {
829 return Private_::ToString_String_Implementation_ (f, options).template As<wstring> ();
838 template <
floating_po
int T, IUNICODECanUnambiguouslyConvertFrom CHAR_T>
841 if constexpr (same_as<remove_cv_t<CHAR_T>,
char>) {
851#if __cpp_lib_to_chars >= 201611 and not qCompilerAndStdLib_from_chars_and_tochars_FP_Precision_Buggy
852#if qCompilerAndStdLib_to_chars_assmes_str_nul_terminated_Buggy
855 auto b = asciiS.begin ();
856 auto e = asciiS.end ();
857#if qCompilerAndStdLib_to_chars_assmes_str_nul_terminated_Buggy
860 if (b != e and *b ==
'+') [[unlikely]] {
863 auto [ptr, ec] = from_chars (b, e, result);
864 if (ec == errc::result_out_of_range) [[unlikely]] {
865 result = *b ==
'-' ? -numeric_limits<T>::infinity () : numeric_limits<T>::infinity ();
867 else if (ec != std::errc{} or ptr != e) [[unlikely]] {
870 result = Private_::ToFloat_RespectingLocale_<T> (s,
nullptr);
873 result = Private_::ToFloat_RespectingLocale_<T> (span<const char>{asciiS},
nullptr);
877 return Private_::ToFloat_RespectingLocale_<T> (s,
nullptr);
879 template <
floating_po
int T, IUNICODECanUnambiguouslyConvertFrom CHAR_T>
880 T ToFloat (span<const CHAR_T> s,
typename span<const CHAR_T>::iterator* remainder)
883 if constexpr (same_as<remove_cv_t<CHAR_T>,
char>) {
890#if __cpp_lib_to_chars >= 201611 and not qCompilerAndStdLib_from_chars_and_tochars_FP_Precision_Buggy
894 char* b = asciiS.begin ();
895 char* e = asciiS.end ();
896 if (b != e and *b ==
'+') [[unlikely]] {
899 auto [ptr, ec] = from_chars (b, e, result);
900 if (ec == errc::result_out_of_range) [[unlikely]] {
901 result = *b ==
'-' ? -numeric_limits<T>::infinity () : numeric_limits<T>::infinity ();
903 else if (ec != std::errc{} or ptr != e) [[unlikely]] {
906 typename span<const CHAR_T>::iterator tmpI;
907 result = Private_::ToFloat_RespectingLocale_<T> (s, &tmpI);
908 ptr = tmpI - s.begin () + b;
911 span{
reinterpret_cast<const char8_t*
> (b),
reinterpret_cast<const char8_t*
> (e)}, ptr - b);
912 Assert (*remainder <= s.end ());
916 return Private_::ToFloat_RespectingLocale_<T> (s, remainder);
918 template <
floating_po
int T,
typename STRINGISH_ARG>
919 inline T
ToFloat (STRINGISH_ARG&& s)
920 requires (IConvertibleToString<STRINGISH_ARG> or is_convertible_v<STRINGISH_ARG, std::string>)
922 using DecayedStringishArg = remove_cvref_t<STRINGISH_ARG>;
923 if constexpr (same_as<DecayedStringishArg, const char*> or same_as<DecayedStringishArg, const char8_t*> or
924 same_as<DecayedStringishArg, const char16_t*> or same_as<DecayedStringishArg, const char32_t*> or
925 same_as<DecayedStringishArg, const wchar_t*>) {
926 return ToFloat<T> (span{s, CString::Length (s)});
928 else if constexpr (same_as<DecayedStringishArg, String>) {
929 Memory::StackBuffer<wchar_t> ignored;
930 auto sp = s.template GetData<wchar_t> (&ignored);
931 return ToFloat<T> (sp);
933 else if constexpr (is_convertible_v<DecayedStringishArg, std::string>) {
935 return ToFloat<T> (span{ss.data (), ss.size ()});
938 return ToFloat<T> (String{forward<STRINGISH_ARG> (s)});
941 template <
floating_po
int T>
942 inline T
ToFloat (
const String& s, String* remainder)
945 Memory::StackBuffer<wchar_t> ignored;
946 span<const wchar_t> srcSpan = s.GetData (&ignored);
947 span<const wchar_t>::iterator tmpRemainder;
948 auto result = ToFloat<T> (srcSpan, &tmpRemainder);
949 *remainder = String{srcSpan.subspan (tmpRemainder - srcSpan.begin ())};
955 template <
typename T>
956 [[deprecated (
"Since Stroika v3.0d1 - use span overload")]]
inline T
ToFloat (
const wchar_t* start,
const wchar_t* end,
const wchar_t** remainder)
958 Require (start <= end);
961#if __cpp_lib_to_chars >= 201611 and not qCompilerAndStdLib_from_chars_and_tochars_FP_Precision_Buggy
966 Memory::StackBuffer<char> asciiS;
968 auto b = asciiS.begin ();
970 auto e = asciiS.end ();
971 if (remainder !=
nullptr) [[unlikely]] {
973 while (b != e and iswspace (*b)) [[unlikely]] {
977 if (b != e and *b ==
'+') [[unlikely]] {
981 auto [ptr, ec] = from_chars (b, e, result);
982 if (ec == errc::result_out_of_range) [[unlikely]] {
983 return *b ==
'-' ? -numeric_limits<T>::infinity () : numeric_limits<T>::infinity ();
986 if (ec != std::errc{}) [[unlikely]] {
987 result = Math::nan<T> ();
991 if (remainder ==
nullptr) [[likely]] {
992 if (ptr != e) [[unlikely]] {
993 result = Math::nan<T> ();
997 *remainder = ptr - originalB + start;
1001 result = Private_::ToFloat_ViaStrToD_<T> (start, end, remainder);
1004 result = Private_::ToFloat_ViaStrToD_<T> (start, end, remainder);
1008 template <
typename T =
double>
1009 [[deprecated (
"Since Stroika v3.0d1 - use span overload")]]
inline T
ToFloat (
const char* start,
const char* end)
1011 return ToFloat<T> (span{start, end});
1013 template <
typename T =
double>
1014 [[deprecated (
"Since Stroika v3.0d1 - use span overload")]]
inline T
ToFloat (
const wchar_t* start,
const wchar_t* end)
1016 return ToFloat<T> (span{start, end});
#define RequireNotReached()
#define RequireNotNull(p)
#define AssertNotReached()
#define qStroika_ATTRIBUTE_INDETERMINATE
qStroika_ATTRIBUTE_INDETERMINATE is used where you would use a C++ attribute for a variable that is i...
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...