Stroika Library 3.0d23
 
Loading...
Searching...
No Matches
FloatConversion.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Characters_FloatConversion_h_
5#define _Stroika_Foundation_Characters_FloatConversion_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <ios>
10#include <locale>
11#include <optional>
12
14#include "Stroika/Foundation/Common/Common.h"
15
16/**
17 * TODO:
18 * @todo REWRITE much of the backend impl, once 3.0 alpha or beta starts. I think API here OK, but impl is weak (and probably slow).
19 *
20 * @todo ToFloat code needs OPTIONS optional argument, to support locales etc.
21 *
22 * @todo Then maybe we can lose ios::format_flags option (maybe keep as ARG, but just grab these fields). Maybe name OK as is, but
23 * just add option for "FIXEDWIDTH", and keep idea of changeing backended arg for ios_flags...
24 */
25
27
28 /**
29 * Control needless trailing zeros. For example, 3.000 instead of 3, or 4.2000 versus 4.2.
30 *
31 * Sometimes eDontTrimZeros desirable (to show precision): but often not.
32 */
33 enum class [[deprecated ("Since Stroika v3.0d23 use FloatFormatType 'trim' variant instead")]] TrimTrailingZerosType {
34 eTrimZeros,
35 eDontTrimZeros,
36
37 Stroika_Define_Enum_Bounds (eTrimZeros, eDontTrimZeros)
38 };
40 DISABLE_COMPILER_GCC_WARNING_START ("GCC diagnostic ignored \"-Wdeprecated-declarations\"");
41 DISABLE_COMPILER_CLANG_WARNING_START ("clang diagnostic ignored \"-Wdeprecated-declarations\"");
42 using TrimTrailingZerosType::eDontTrimZeros;
43 using TrimTrailingZerosType::eTrimZeros;
44 DISABLE_COMPILER_MSC_WARNING_END (4996)
45 DISABLE_COMPILER_GCC_WARNING_END ("GCC diagnostic ignored \"-Wdeprecated-declarations\"");
46 DISABLE_COMPILER_CLANG_WARNING_END ("clang diagnostic ignored \"-Wdeprecated-declarations\"");
47
48 enum class PredefinedLocale {
49 /**
50 * Use the hardwired 'C' locale (not exactly a real locale in the sense of localization. But a good choice
51 * for the 'locale' to use for non-localized code (e.g. much serverside code).
52 */
54
55 /**
56 * \note - this selects the current locale at the time the preference is used, whereas
57 * in Stroika v2.1, it used the current locale at the time the preference object was created.
58 */
60 };
63
64 /**
65 * Significant figures are the digits in a measured or calculated value that carry reliable information
66 * regarding its precision and accuracy, typically including all non-zero digits, zeros between non-zero digits, and trailing zeros in a decimal.
67 * They define the limit of a measurement's certainty.
68 *
69 * This is used for specifying how to format floating point numbers.
70 *
71 * @alias Precision (this was called 'Precision' before Stroika v3.0d23)
72 *
73 * Rules:
74 * o Non-zero Digits: All digits from 1-9 are always significant (e.g., \‍(45.2\‍) has 3).
75 * o Interior Zeros: Zeros between non-zero digits are always significant (e.g., \‍(1002\‍) has 4).
76 * o Leading Zeros: Zeros to the left of the first non-zero digit are never significant; they are placeholders (e.g., \‍(0.0032\‍) has 2).
77 * o Trailing Zeros (Decimal Present): Zeros at the end of a number that contains a decimal point are significant (e.g., \‍(92.00\‍) has 4).
78 * o Trailing Zeros (No Decimal): Zeros at the end of a number without a decimal point are ambiguous and usually not significant (e.g., \‍(140\‍) has 2), unless indicated by a decimal point (e.g., \‍(140.\‍) has 3).
79 * o Exact Numbers: Numbers from counting or definitions (e.g., \‍(12\‍) inches in a foot) have an infinite number of significant figures.
80 * o Scientific Notation: In \‍(A\times 10^{b}\‍), all digits in the coefficient (\‍(A\‍)) are significant (e.g., \‍(1.020\times 10^{3}\‍) has 4).
81 *
82 * Examples:
83 * "3.01" => 3
84 * "03.01" => 3
85 * "-44.21" => 4
86 * "+44.21" => 4
87 * "-44.21e2" => 4
88 * "-44.210e2" => 5
89 * "400" => 1
90 * "400." => 3
91 * "400.0" => 4
92 * "0.0000001234567" => 7
93 * "0.000000000" => 9 ; a bit ambiguous given the rules - leading zeros vs trailing zeros (decimal present)
94 *
95 * \note - This differs from the iostream library 'precision' where:
96 * Its exact meaning depends on whether the stream is using the default floating-point notation or the std::fixed or std::scientific
97 * Default Notation (defaultfloat): The precision value specifies the total number of significant digits to display.
98 * Fixed or Scientific Notation (fixed, scientific): The precision value specifies the exact number of digits to appear after the decimal point
99 *
100 * The special value SignificantFigures::kFullPrecision refers to when you wish the full precision that allows the exact value to be read back
101 * after being written:
102 *
103 * \note - The c++ float 'precision' is always 1 less than the number of significant figures (since format is always N.xxxxeWW).
104 * Only possible slight exception would be for the number zero, where I'm not quite sure, but I think this is roughly right then too.
105 *
106 * \note
107 * \see https://stackoverflow.com/questions/22458355/what-is-the-purpose-of-max-digits10-and-how-is-it-different-from-digits10
108 * Roughly:
109 * o digits10 is the number of decimal digits guaranteed to survive text → float → text round-trip.
110 * o max_digits10 is the number of decimal digits needed to guarantee correct float → text → float round-trip.
111 *
112 * TODO:
113 * @todo rewrite with https://en.cppreference.com/w/cpp/utility/to_chars
114
115 */
117 public:
118 using RepType = unsigned int; // maybe use uint16_t?
119
120 public:
121 /**
122 * Flag indicating full precision (see Precision class docs for explanation) - max_digits10
123 */
124 enum FullFlag {
125 eFull
126 };
127
128 public:
129 /**
130 * SignificantFigures ()/0
131 * Same as kDefault: 6
132 * SignificantFigures(FullFlag):
133 * special magic value, so depending on type 'T' in call to GetEffectiveSignificantFigures () - gets full precision for that type
134 */
135 constexpr SignificantFigures () = default;
136 constexpr SignificantFigures (RepType p);
137 constexpr SignificantFigures (FullFlag);
138
139 public:
140 constexpr bool operator== (const SignificantFigures&) const = default;
141
142 public:
143 /**
144 * Return the used precision. This will be the value specified in the Precision constructor, or
145 * if 'Full' precision given in the constructor, the desired floating point type will be used to compute
146 * the appropriate precision.
147 *
148 * NOTE - its NOT super clear what this should be. But our definition of it is sufficient precision so that when
149 * you read the value back in, (serialize then deserialize) - you get the original value.
150 *
151 * Docs like https://en.cppreference.com/w/cpp/types/numeric_limits/digits10 - seem to suggest this might be
152 * std::numeric_limits<T>::digits10 or std::numeric_limits<T>::digits10-1 for floating point types.
153 *
154 * Docs like https://en.cppreference.com/w/cpp/io/manip/setprecision - suggest it might be:
155 * std::numeric_limits<T>::digits10 + 1;
156 *
157 * Docs like https://en.cppreference.com/w/cpp/utility/to_chars - (3) - suggest none of the above - you must call
158 * to_chars() without specifying a precision.
159 *
160 * https://stackoverflow.com/questions/22458355/what-is-the-purpose-of-max-digits10-and-how-is-it-different-from-digits10
161 * Selected numeric_limits<T>::max_digits10 cuz used to map float -> text, and then hopefully someday back to float value preserving
162 */
163 template <floating_point T>
164 constexpr RepType GetEffectiveSignificantFigures () const;
165
166 public:
167 /**
168 * @see Characters::ToString ();
169 */
170 nonvirtual String ToString () const;
171
172 public:
173 /**
174 * kDefault is 6
175 */
177
178 public:
179 /**
180 * @brief Sentinal value that is interpretted differently depending on the type passed to GetEffectiveSignificantFigures()
181 */
183
184 public:
185 /**
186 * Calculate the precision - number of significant digits - in the given number. For this purpose, count trailing
187 * zeros. So basically string length, minus 1 for '.', minus 1 for any leading +-, minus any characters in exponential
188 * specifier.
189 */
190 template <IStdBasicStringCompatibleCharacter CHAR>
191 static constexpr auto Calculate (span<const CHAR> number) -> RepType;
192
193 private:
194 /**
195 * Internally treat fSignificantFigures_ as meaning kFullPrecision
196 */
197 optional<RepType> fSignificantFigures_{6};
198 };
199
200 using Precision [[deprecated ("Since Stroika v3.0d23 use SignificantFigures instead")]] =
201 SignificantFigures; // for backward compatibility - but maybe should be removed in future
202
204 DISABLE_COMPILER_GCC_WARNING_START ("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
205 DISABLE_COMPILER_CLANG_WARNING_START ("clang diagnostic ignored \"-Wdeprecated-declarations\"")
206
207 /**
208 */
209 enum class FloatFormatType {
210
211 /**
212 * corresponds to unsetf (floatfield) - which may be different than scientific or fixed point.
213 * This is basically what the 'C' standard decided would be the default way to format floating point numbers.
214 *
215 * Precision Field: The stream's precision setting (managed by std::setprecision or std::ios_base::precision())
216 * specifies the maximum number of meaningful digits to display in total (both before and after the decimal point),
217 * not a fixed number of digits after the decimal point.
218 *
219 * Trailing Zeros: It does not pad the output with trailing zeros if the number can be displayed with
220 * fewer digits than the precision.
221 *
222 * Notation: It automatically switches between fixed-point and scientific notation as needed to best
223 * represent the value within the given precision. For example, a large number or a number very close
224 * to zero might be shown in scientific notation, while others will be in fixed-point notation.
225 *
226 * For example (first 3 from https://en.cppreference.com/w/cpp/io/manip/fixed.html)
227 * number │ output
228 * -------------------│----------------------
229 * 0.0 │ 0
230 * 0.01 │ 0.01
231 * 0.00001 │ 1e-05 (probably could be 0.00001)
232 * 3.12e12 │ 3.12e+12
233 * 3.12 │ 3.12
234 * 212312345.0 │ 2.12312e+08
235 * -44.2 │ -44.2
236 * 0.0000001234567 │ 1.23457e-07
237 */
239
240 /**
241 * corresponds to ios_base::fixed (numbers are displayed without an exponent part, not actually fixed with display)
242 *
243 * For example (first 3 from https://en.cppreference.com/w/cpp/io/manip/fixed.html)
244 * number │ output
245 * -------------------│----------------------
246 * 0.0 │ 0.000000
247 * 0.01 │ 0.010000
248 * 0.00001 │ 0.000010
249 * 3.12e12 │ 3120000000000
250 * 3.12 │ 3.12
251 * 212312345.0 │ 212312345
252 * -44.2 │ -44.2
253 * 0.0000001234567 │ 0.000000
254 */
256
257 /**
258 * @brief eFixedPointWithoutWhitespaceTrimmed
259 */
261
262 /**
263 * For example (first 3 from https://en.cppreference.com/w/cpp/io/manip/fixed.html)
264 * number │ output
265 * -------------------│----------------------
266 * 0.0 │ 0
267 * 0.01 │ 0.01
268 * 0.00001 │ 0.00001
269 * 3.12e12 │ 3120000000000
270 * 3.12 │ 3.12
271 * 212312345.0 │ 212312345
272 * -44.2 │ -44.2
273 * 0.0000001234567 │ 0
274 */
276
277 /**
278 * corresponds to ios_base::scientific
279 *
280 * For example (first 3 from https ://en.cppreference.com/w/cpp/io/manip/fixed.html)
281 * number │ output
282 * -------------------│----------------------
283 * 0.0 │ 0.000000e+00
284 * 0.01 │ 1.000000e-02
285 * 0.00001 │ 1.000000e-05
286 * 3.12e12 │ 3.12000e+12
287 * 3.12 │ 3.12000e+00
288 * 212312345.0 │ 2.12312e+08
289 * -44.2 │ -4.42000e+01
290 * 0.0000001234567 │ 1.23457e-07
291 */
293
294 /**
295 * @brief eScientificWithoutWhitespaceTrimmed
296 */
298
299 /**
300 * For example (first 3 from https ://en.cppreference.com/w/cpp/io/manip/fixed.html)
301 * number │ output
302 * -------------------│----------------------
303 * 0.0 │ 0.0e+00
304 * 0.01 │ 1.0e-02
305 * 0.00001 │ 1.0e-05
306 * 3.12e12 │ 3.12e+12
307 * 3.12 │ 3.12e+00
308 * 212312345.0 │ 2.12312e+08
309 * -44.2 │ -4.42e+01
310 * 0.0000001234567 │ 1.23457e-07
311 */
313
314 /**
315 * \brief Somewhat like defaultfloat, but never uses scientific notation.
316 *
317 * not scientific (no e+nn), but otherwise somewhat like fixed point or defaultfloat.
318 *
319 * Really no good name for this (unscientific, defaultfloat means something differnt in C++).
320 * Sort of like defaultfloat but NEVER deciding to use scientific notation.
321 *
322 * For example (first 3 from https://en.cppreference.com/w/cpp/io/manip/fixed.html)
323 * number │ output
324 * -------------------│----------------------
325 * 0.0 │ 0
326 * 0.01 │ 0.01
327 * 0.00001 │ 0.00001
328 * 3.12e12 │ 3120000000000
329 * 3.12 │ 3.12
330 * 212312345.0 │ 212312345
331 * -44.2 │ -44.2
332 * 0.0000001234567 │ 0.000000123457
333 */
334 eStandard,
335
336 eAutomaticScientific [[deprecated ("Since Stroika v3.0d23 use eDefaultFloat instead")]],
337
338 /**
339 * If its good enuf for C, its good enuf for me ;-). Seriously - sensible default.
340 */
342
343 Stroika_Define_Enum_Bounds (eDefaultFloat, eAutomaticScientific)
344 };
345 using FloatFormatType::eAutomaticScientific;
346 DISABLE_COMPILER_MSC_WARNING_END (4996)
347 DISABLE_COMPILER_GCC_WARNING_END ("GCC diagnostic ignored \"-Wdeprecated-declarations\"");
348 DISABLE_COMPILER_CLANG_WARNING_END ("clang diagnostic ignored \"-Wdeprecated-declarations\"");
349 using FloatFormatType::eDefaultFloat;
350 using FloatFormatType::eFixedPoint;
351 using FloatFormatType::eFixedPointWithWhitespaceTrimmed;
352 using FloatFormatType::eScientific;
353 using FloatFormatType::eScientificWithWhitespaceTrimmed;
354 using FloatFormatType::eStandard;
355
356 /**
357 * These are options for the FloatConversion::ToString () function
358 *
359 * FloatConversion::ToString uses the locale specified by ToStringOptions, but defaults to
360 * the "C" locale.
361 *
362 * This prints and trims any trailing zeros (after the decimal point - fTrimTrailingZeros -
363 * by default.
364 *
365 * Float2String () maps NAN values to the string "NAN", and negative infinite values to "-INF", and positive infinite
366 * values to "INF" (ignoring case).
367 * @see http://en.cppreference.com/w/cpp/string/byte/strtof
368 */
370 public:
371 /**
372 * Default is to use use C-locale
373 * \note - if ios_base::fmtflags are specified, these REPLACE - not merged - with
374 * basic ios flags
375 */
376 constexpr ToStringOptions () = default;
377 constexpr ToStringOptions (PredefinedLocale p);
378 ToStringOptions (const locale& l);
379 constexpr ToStringOptions (ios_base::fmtflags fmtFlags);
380 constexpr ToStringOptions (SignificantFigures precision);
381 constexpr ToStringOptions (FloatFormatType floatFormat);
383 DISABLE_COMPILER_GCC_WARNING_START ("GCC diagnostic ignored \"-Wdeprecated-declarations\"");
384 DISABLE_COMPILER_CLANG_WARNING_START ("clang diagnostic ignored \"-Wdeprecated-declarations\"");
385 [[deprecated ("Since Stroika v3.0d23 use appropriate FloatFormatType 'trim' variant instead")]] constexpr ToStringOptions (TrimTrailingZerosType trimTrailingZeros);
386 DISABLE_COMPILER_MSC_WARNING_END (4996)
387 DISABLE_COMPILER_GCC_WARNING_END ("GCC diagnostic ignored \"-Wdeprecated-declarations\"");
388 DISABLE_COMPILER_CLANG_WARNING_END ("clang diagnostic ignored \"-Wdeprecated-declarations\"");
389 constexpr ToStringOptions (const ToStringOptions& b1, const ToStringOptions& b2);
390 template <typename... ARGS>
391 constexpr ToStringOptions (const ToStringOptions& b1, const ToStringOptions& b2, ARGS&&... args);
392
393 public:
394 constexpr optional<SignificantFigures> GetSignificantFigures () const;
395
396 public:
397 [[deprecated ("Since Stroika v3.0d23 use FloatFormatType 'trim' variant instead")]]
398 constexpr optional<bool> GetTrimTrailingZeros () const;
399
400 public:
401 /**
402 * \brief return the selected locale object
403 *
404 * \note before Stroika v3.0d1, this returned optional, and for the case of locale::classic, it retuned none
405 */
406 nonvirtual locale GetUseLocale () const;
407
408 public:
409 /**
410 * \brief return true if locale used is locale::classic() - the 'C' locale; mostly used as optimization/special case
411 */
412 nonvirtual bool GetUsingLocaleClassic () const;
413
414 public:
415 constexpr optional<FloatFormatType> GetFloatFormat () const;
416
417 public:
418 constexpr optional<ios_base::fmtflags> GetIOSFmtFlags () const;
419
420 public:
421 [[deprecated ("Since Stroika v3.0d23 use FloatFormatType 'trim' variant instead")]]
422 static constexpr bool kDefaultTrimTrailingZeros{true};
423
424 public:
425 /**
426 * @see Characters::ToString ();
427 */
428 nonvirtual String ToString () const;
429
430 private:
431 optional<SignificantFigures> fSignificantFigures_;
432 optional<ios_base::fmtflags> fFmtFlags_;
433 bool fUseCurrentLocale_{false}; // dynamically calculated current locale
434 optional<locale> fUseLocale_; // if missing, use locale::classic (unless fUseCurrentLocale_)
435 optional<bool> fTrimTrailingZeros_; // keep while deprecated
436 optional<FloatFormatType> fFloatFormat_;
437 };
438
439 /**
440 * ToString converts a floating point number to a string, controlled by parameterized options.
441 *
442 * @see ToStringOptions
443 *
444 * ToString () maps NAN values to the string "NAN", and negative infinite values to "-INF", and positive infinite
445 * values to "INF" (note NAN/INF are case insensitive).
446 * @see http://en.cppreference.com/w/cpp/string/byte/strtof
447 *
448 * The supported type values for FLOAT_TYPE are:
449 * o float
450 * o double
451 * o long double
452 *
453 * The supported type values for RESULT_TYPE are:
454 * o String
455 * o string
456 * o wstring
457 * o ... but this could sensibly be extended in the future
458 */
459 template <Common::IAnyOf<String, string, wstring> STRING_TYPE = String, floating_point FLOAT_TYPE = float>
460 STRING_TYPE ToString (FLOAT_TYPE f, const ToStringOptions& options = {});
461
462 /**
463 * ToFloat all overloads:
464 * Convert the given decimal-format floating point string to an float, double, or long double.
465 *
466 * ToFloat will return nan () if no valid parse (for example, -1.#INF000000000000 is,
467 * invalid and returns nan, despite the fact that this is often emitted by the MSFT sprintf() for inf values).
468 *
469 * The overloads taking string or const char* arguments Require() that the input is ASCII ('C' locale required/assumed).
470 * (@todo revisit this point --LGP 2022-12-28)
471 *
472 * If the argument value is too large or too small to fit in 'T' (ERANGE) - then the value will be
473 * pinned to -numeric_limits<T>::infinity () or numeric_limits<T>::infinity ().
474 *
475 * If the input string is INF or INFINITY (with an optional +/- prefix) - the returned
476 * value will be the appropriate version of infinity.
477 *
478 * If the argument is the string "NAN", a quiet NAN will be returned. If the string -INF or -INFINITY,
479 * a negative infinite float will be returned, and if INF or INFINITY is passed, a positive infinite
480 * value will be returned:
481 * @see http://en.cppreference.com/w/cpp/string/byte/strtof
482 *
483 * @todo TBD/TOCHANGE if using strtod or from_chars - about to add OPTIONS PARAM to decide
484 * For now - tries both
485 *
486 * ToFloat (no remainder parameter):
487 * The argument should be pre-trimmed (whitespace). If there is any leading or trailing garbage (even whitespace)
488 * this function will return nan() (**note - unlike overload with 'remainder' arg**).
489 *
490 * ToFloat (with remainder parameter):
491 * Logically a simple wrapper on std::wcstof, std::wcstod, std::wcstold - except using String class, and returns the
492 * unused portion of the string in the REQUIRED remainder OUT parameter.
493 *
494 * This means it ALLOWS leading whitespace (skipped). And it allows junk at the end (remainder parameter filled in with what).
495 *
496 * \note SEE http://stroika-bugs.sophists.com/browse/STK-748
497 * We will PROBABLY change this API to take a ToFloatOptions parameter to handle proper locale/conversions of strings to numbers
498 * but so far I've not been able to get any of that working, so ignore for now...
499 *
500 * \pre start <= end; for overloads with start/end, and must point to valid string in that range
501 * \pre remainder != nullptr
502 *
503 * // @todo redo all these with some concept to make it shorter - like ISCOVNERTIBLE TO STRING
504 *
505 *
506 * \note when called with CHAR_T=char, we REQUIRE the argument string is ALL ASCII
507 */
508 template <floating_point T = double, IUNICODECanUnambiguouslyConvertFrom CHAR_T>
509 T ToFloat (span<const CHAR_T> s);
510 template <floating_point T = double, IUNICODECanUnambiguouslyConvertFrom CHAR_T>
511 T ToFloat (span<const CHAR_T> s, typename span<const CHAR_T>::iterator* remainder);
512 template <floating_point T = double, typename STRINGISH_ARG>
513 T ToFloat (STRINGISH_ARG&& s)
514 requires (IConvertibleToString<STRINGISH_ARG> or is_convertible_v<STRINGISH_ARG, std::string>);
515 template <floating_point T = double>
516 T ToFloat (const String& s, String* remainder);
517
518}
519
522}
523
524/*
525 ********************************************************************************
526 ***************************** Implementation Details ***************************
527 ********************************************************************************
528 */
529#include "FloatConversion.inl"
530
531#endif /*_Stroika_Foundation_Characters_FloatConversion_h_*/
#define Stroika_Define_Enum_Bounds(FIRST_ITEM, LAST_ITEM)
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
@ eStandard
Somewhat like defaultfloat, but never uses scientific notation.
STRING_TYPE ToString(FLOAT_TYPE f, const ToStringOptions &options={})
static const SignificantFigures kFullPrecision
Sentinal value that is interpretted differently depending on the type passed to GetEffectiveSignifica...
static constexpr auto Calculate(span< const CHAR > number) -> RepType