Stroika Library 3.0d18
 
Loading...
Searching...
No Matches
FloatConversion.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Foundation/StroikaPreComp.h"
5
6#include <charconv>
7#include <cstdarg>
8#include <cstdlib>
9#include <iomanip>
10#include <limits>
11#include <sstream>
12
15#include "Stroika/Foundation/Containers/Common.h"
19#include "Stroika/Foundation/Math/Common.h"
20
21#include "FloatConversion.h"
22
23using namespace Stroika::Foundation;
25using namespace Stroika::Foundation::Memory;
26
27/*
28 ********************************************************************************
29 ************************ FloatConversion::Precision ****************************
30 ********************************************************************************
31 */
33{
35 sb << "{"sv;
36 if (fPrecision_) {
37 sb << "Precision:"sv << *fPrecision_;
38 }
39 else {
40 sb << "FULL"sv;
41 }
42 sb << "}"sv;
43 return sb;
44}
45
46/*
47 ********************************************************************************
48 ******************** FloatConversion::ToStringOptions **************************
49 ********************************************************************************
50 */
52{
54 sb << "{"sv;
55 if (fPrecision_) {
56 sb << "Precision:"sv << *fPrecision_ << ","sv;
57 }
58 if (fFmtFlags_) {
59 sb << "Fmt-Flags:"sv << Characters::ToString ((int)*fFmtFlags_, ios_base::hex) << ","sv;
60 }
61 if (fUseLocale_) {
62 sb << "Use-Locale"sv << String::FromNarrowSDKString (fUseLocale_->name ()) << ","sv;
63 }
64 if (fTrimTrailingZeros_) {
65 sb << "Trim-Trailing-Zeros: "sv << *fTrimTrailingZeros_ << ","sv;
66 }
67 if (fFloatFormat_) {
68 sb << "Scientific-Notation: "sv << (int)*fFloatFormat_ << ","sv;
69 }
70 sb << "}"sv;
71 return sb;
72}
73
74namespace {
75 inline char* mkFmtWithPrecisionArg_ (char* formatBufferStart, [[maybe_unused]] char* formatBufferEnd, char _Spec)
76 {
77 char* fmtPtr = formatBufferStart;
78 *fmtPtr++ = '%';
79 // include precision arg
80 *fmtPtr++ = '.';
81 *fmtPtr++ = '*';
82 if (_Spec != '\0') {
83 *fmtPtr++ = _Spec; // e.g. 'L' qualifier
84 }
85 *fmtPtr++ = 'g'; // format specifier
86 *fmtPtr = '\0';
87 Require (fmtPtr < formatBufferEnd);
88 return formatBufferStart;
89 }
90
91 template <typename FLOAT_TYPE>
92 inline String Float2String_GenericCase_ (FLOAT_TYPE f, const FloatConversion::ToStringOptions& options)
93 {
94 Require (not isnan (f));
95 Require (not isinf (f));
96
97 // expensive to construct, and slightly cheaper to just use thread_local version of
98 // the same stringstream each time (only one per thread can be in use)
99 static thread_local stringstream s;
100 static const ios_base::fmtflags kDefaultIOSFmtFlags_ = s.flags (); // Just copy out of the first constructed stringstream
101
102 s.str (string{});
103 s.clear ();
104
105 s.imbue (options.GetUseLocale ());
106
107 // must set explicitly (even if defaulted) because of the thread_local thing
108 s.flags (options.GetIOSFmtFlags ().value_or (kDefaultIOSFmtFlags_));
109
110 // todo must set default precision because of the thread_local thing
111 unsigned int usePrecision = options.GetPrecision ().value_or (FloatConversion::Precision{}).GetEffectivePrecision<FLOAT_TYPE> ();
112 s.precision (usePrecision);
113
114 {
115 optional<ios_base::fmtflags> useFloatField;
116 switch (options.GetFloatFormat ().value_or (FloatConversion::FloatFormatType::eDEFAULT)) {
117 case FloatConversion::FloatFormatType::eScientific:
118 useFloatField = ios_base::scientific;
119 break;
120 case FloatConversion::FloatFormatType::eDefaultFloat:
121 break;
122 case FloatConversion::FloatFormatType::eFixedPoint:
123 useFloatField = ios_base::fixed;
124 break;
125 case FloatConversion::FloatFormatType::eAutomaticScientific: {
126 bool useScientificNotation = abs (f) >= pow (10, usePrecision / 2) or
127 (f != 0 and abs (f) < pow (10, -static_cast<int> (usePrecision) / 2)); // scientific preserves more precision - but non-scientific looks better
128 if (useScientificNotation) {
129 useFloatField = ios_base::scientific;
130 }
131 } break;
132 default:
134 break;
135 }
136 if (useFloatField) {
137 s.setf (*useFloatField, ios_base::floatfield);
138 }
139 else {
140 s.unsetf (ios_base::floatfield); // see std::defaultfloat - not same as ios_base::fixed
141 }
142 }
143
144 s << f;
145
146 String tmp = options.GetUsingLocaleClassic () ? String{s.str ()} : String::FromNarrowString (s.str (), options.GetUseLocale ());
147 if (options.GetTrimTrailingZeros ().value_or (FloatConversion::ToStringOptions::kDefaultTrimTrailingZeros)) {
148 Characters::FloatConversion::Private_::TrimTrailingZeros_ (&tmp);
149 }
150 return tmp;
151 }
152 template <typename FLOAT_TYPE>
153 inline String Float2String_ (FLOAT_TYPE f, const FloatConversion::ToStringOptions& options)
154 {
155 switch (fpclassify (f)) {
156 case FP_INFINITE: {
157 Assert (isinf (f));
158 Assert (!isnan (f));
159 static const String kNEG_INF_STR_{"-INF"_k};
160 static const String kINF_STR_{"INF"_k};
161 return f > 0 ? kINF_STR_ : kNEG_INF_STR_;
162 }
163 case FP_NAN: {
164 Assert (isnan (f));
165 Assert (!isinf (f));
166 static const String kNAN_STR_{"NAN"_k};
167 return kNAN_STR_;
168 }
169 default: {
170 if constexpr (qCompilerAndStdLib_valgrind_fpclassify_check_Buggy && qStroika_Foundation_Debug_AssertionsChecked) {
171 if (Debug::IsRunningUnderValgrind ()) {
172 if (isinf (f)) {
173 DbgTrace ("fpclassify ({}) = {}"_f, (double)f, fpclassify (f));
174 static const String kNEG_INF_STR_{"-INF"_k};
175 static const String kINF_STR_{"INF"_k};
176 return f > 0 ? kINF_STR_ : kNEG_INF_STR_;
177 }
178 }
179 }
180 }
181 }
182 Assert (!isnan (f));
183 Assert (!isinf (f));
184 return Float2String_GenericCase_<FLOAT_TYPE> (f, options);
185 }
186}
#define RequireNotReached()
Definition Assertions.h:385
#define qStroika_Foundation_Debug_AssertionsChecked
The qStroika_Foundation_Debug_AssertionsChecked flag determines if assertions are checked and validat...
Definition Assertions.h:48
#define DbgTrace
Definition Trace.h:309
Include this file VERY EARLY ON - before including stuff like <cstdio> - to allow use of Valgrind (so...
Similar to String, but intended to more efficiently construct a String. Mutable type (String is large...
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
static String FromNarrowString(const char *from, const locale &l)
Definition String.inl:340
static String FromNarrowSDKString(const char *from)
Definition String.inl:470
String ToString(T &&t, ARGS... args)
Return a debug-friendly, display version of the argument: not guaranteed parsable or usable except fo...
Definition ToString.inl:465
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...