BDE 4.39.x Production Release
Loading...
Searching...
No Matches
bslfmt_formatterfloating.h
Go to the documentation of this file.
1/// @file bslfmt_formatterfloating.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bslfmt_formatterfloating.h -*-C++-*-
8
9#ifndef INCLUDED_BSLFMT_FORMATTERFLOATING
10#define INCLUDED_BSLFMT_FORMATTERFLOATING
11
12#include <bsls_ident.h>
13BSLS_IDENT("$Id: $")
14
15/// @defgroup bslfmt_formatterfloating bslfmt_formatterfloating
16/// @brief Provide a formatter customization for floating point types
17/// @addtogroup bsl
18/// @{
19/// @addtogroup bslfmt
20/// @{
21/// @addtogroup bslfmt_formatterfloating
22/// @{
23///
24/// <h1> Outline </h1>
25/// * <a href="#bslfmt_formatterfloating-purpose"> Purpose</a>
26/// * <a href="#bslfmt_formatterfloating-classes"> Classes </a>
27/// * <a href="#bslfmt_formatterfloating-canonical-header"> Canonical Header </a>
28/// * <a href="#bslfmt_formatterfloating-description"> Description </a>
29/// * <a href="#bslfmt_formatterfloating-long-double"> long double </a>
30/// * <a href="#bslfmt_formatterfloating-usage"> Usage </a>
31/// * <a href="#bslfmt_formatterfloating-example-formatting-a-floating-point-number"> Example: Formatting a floating point number </a>
32///
33/// # Purpose {#bslfmt_formatterfloating-purpose}
34/// Provide a formatter customization for floating point types
35///
36/// # Classes {#bslfmt_formatterfloating-classes}
37///
38/// - bsl::formatter<float, t_CHAR>: formatter template for `float`
39/// - bsl::formatter<double, t_CHAR>: formatter template for `double`
40/// - bsl::formatter<long double, t_CHAR>: disabled formatter template
41///
42/// # Canonical Header {#bslfmt_formatterfloating-canonical-header}
43/// bsl_format.h
44///
45/// # Description {#bslfmt_formatterfloating-description}
46/// This component provides partial specializations of
47/// `bsl::formatter` catering for floating point types. The component defines a
48/// component-private `bslfmt::FormatterFloating_Base` class that implements the
49/// formatters for the `float` and `double` types, for both supported output
50/// character types (`char` and `wchar_t`). Partial specializations are then
51/// defined that use `bslfmt::FormatterFloating_Base` to specify the actual
52/// formatters in the `bsl` namespace. Notice that `long double` is not one of
53/// the supported types (see @ref bslfmt_formatterfloating-long-double ).
54///
55/// This header is not intended to be included directly. Please include
56/// `<bsl_format.h>` to be able to use specializations of the `bsl::formatter`
57/// for floating point types.
58///
59/// ## long double {#bslfmt_formatterfloating-long-double}
60///
61///
62/// The type `long double` is rarely used in code as it is not only not
63/// guaranteed to provide more precision than `double`, but in fact it is
64/// exactly the same type as `double` on MSVC (Microsoft Windows), which makes
65/// any code that uses it for more precision non-portable.
66///
67/// We also do not have a ryu library implementation for `long double` so we
68/// could only use much slower means of "printing" it than the other two types.
69///
70/// For the above reasons `long double` is now not supported and an attempt to
71/// print it will result in a runtime error.
72///
73/// ## Usage {#bslfmt_formatterfloating-usage}
74///
75///
76/// In this section we show the intended use of this component.
77///
78/// ### Example: Formatting a floating point number {#bslfmt_formatterfloating-example-formatting-a-floating-point-number}
79///
80///
81/// We do not expect most users of `bsl::format` to interact with this type
82/// directly and instead use `bsl::format` or `bsl::vformat`, so this example is
83/// necessarily unrealistic.
84///
85/// Suppose we want to test pointer formatter's ability to format a number in
86/// hexadecimal format with defined alignment and padding.
87///
88/// @code
89/// bslfmt::MockParseContext<char> mpc("*<8a", 1);
90///
91/// bsl::formatter<double, char> f;
92/// mpc.advance_to(f.parse(mpc));
93///
94/// const double value = 42.24;
95///
96/// bslfmt::MockFormatContext<char> mfc(value, 0, 0);
97///
98/// mfc.advance_to(bsl::as_const(f).format(value, mfc));
99///
100/// assert("1.51eb851eb851fp+5" == mfc.finalString());
101/// @endcode
102/// @}
103/** @} */
104/** @} */
105
106/** @addtogroup bsl
107 * @{
108 */
109/** @addtogroup bslfmt
110 * @{
111 */
112/** @addtogroup bslfmt_formatterfloating
113 * @{
114 */
115
116#include <bslscm_version.h>
117
118#include <bslfmt_formaterror.h>
119#include <bslfmt_formatterbase.h>
121#include <bslfmt_padutil.h>
123
124#include <bsla_annotations.h>
125
127
129
130#include <bsls_assert.h>
131#include <bsls_exceptionutil.h>
132#include <bsls_keyword.h>
133
134
135namespace bslfmt {
136
137 // =============================
138 // struct FormatterFloating_Base
139 // =============================
140
141/// This class template provides the implementation for all possible floating
142/// point formatting styles and the parsing of the format specification. The
143/// specified `t_VALUE` template type argument determines the type of floating
144/// point value use, while the specified `t_CHAR` determines the output's character type.
145///
146/// \pre The behavior is undefined unless `t_VALUE` is one of
147/// `float` or `double`, and `t_CHAR` is on of `char` or `wchar_t`.
148///
149/// See @ref bslfmt_formatterfloating
150template <class t_VALUE, class t_CHAR>
152 private:
153 // PRIVATE TYPES
154
155 /// A type alias for the `StandardFormatSpecification<t_CHAR>`.
157
158 // DATA
159 Specification d_spec; /// Parsed specification.
160
161 /// A constant representing the case when a position is not found in a
162 /// string-search.
163 static const size_t k_NO_POS = (size_t)(-1);
164
165 private:
166 // PRIVATE CLASS METHODS
167
168 /// Convert the default format textual representation of a floating point
169 /// number to its alternative format in the specified `buf` of size
170 /// `numberLength` by adding a decimal point if necessary and return the new size of the buffer.
171 ///
172 /// \pre The behavior is undefined unless the buffer
173 /// contains a floating point value in its default format, and it is large
174 /// enough to store the new (dot) character added.
175 static size_t applyDefaultAlternate(char* buf, size_t numberLength);
176
177 /// Convert the fixed format textual representation of a floating point
178 /// number to its alternative format in the specified `buf` of size
179 /// `numberLength` and return the new size of the buffer.
180 ///
181 /// \pre The behavior is undefined unless the buffer contains a floating point value in its
182 /// fixed format, and it is large enough to store the new (dot) character
183 /// added.
184 static size_t applyFixedAlternate(char *buf, size_t numberLength);
185
186 /// Convert the general format textual representation of a floating point
187 /// number to its alternative format in the specified `buf` of size
188 /// `numberLength`, using the specified `precision` to add back removed
189 /// trailing zeros and return the new size of the buffer.
190 ///
191 /// \pre The behavior is undefined unless the buffer contains a floating point value in its
192 /// general format, and it is large enough to store the new characters
193 /// added. The behavior is also undefined unless `precision >= 0`.
194 static
195 size_t applyGeneralAlternate(char *buf,
196 size_t numberLength,
197 int precision);
198
199 /// In case there is a decimal point in the scientific or hexadecimal
200 /// format textual representation of a number in the specified `buf` filled
201 /// up to the specified `numberLength` do nothing and return
202 /// `numberLength`. In case there is no decimal point in the `buf` insert
203 /// one right before the specified `expChar` position and return
204 /// `numberLength + 1`. If no `expChar` is present in `buf` (assume that
205 /// the value is not numeric but NaN or infinity) do nothing and return `numberLength`.
206 ///
207 /// \pre The behavior is undefined unless `expChar` is `e` when
208 /// `buf` contains a number in scientific format, or `p` when it is a
209 /// number in hexfloat format. The behavior is also undefined unless the
210 /// buffer is long enough to contain the additional (dot) character.
211 static
212 size_t applyScientificAlternate(char expChar,
213 char *buf,
214 size_t numberLength);
215
216 // PRIVATE MANIPULATORS
217
218 /// Write the specified `numberBuffer` of size `numberLength` aligned with
219 /// fills according to the specified `finalSpec` to the output iterator
220 /// of the `formatContext` and return an iterator one-past the last
221 /// written.
222 template <class t_FORMAT_CONTEXT>
223 typename t_FORMAT_CONTEXT::iterator alignAndCopy(
224 const char *numberBuffer,
225 size_t numberLength,
226 t_FORMAT_CONTEXT& formatContext,
227 const Specification& finalSpec) const;
228
229 /// Create the fixed string representation of the specified `value`,
230 /// customized in accordance with the requested format in the specified
231 /// `finalSpec`, and write the result to the output iterator that the `formatContext` points to.
232 ///
233 /// \pre The behavior is undefined unless the
234 /// requested format is fixed or uppercase fixed.
235 template <class t_FORMAT_CONTEXT>
236 typename t_FORMAT_CONTEXT::iterator formatFixedImpl(
237 t_VALUE value,
238 t_FORMAT_CONTEXT& formatContext,
239 const Specification& finalSpec) const;
240
241 /// Create the minimal-round-tripping string representation of the
242 /// specified `value`, customized in accordance with the requested format
243 /// in the specified `finalSpec`, and write the result to the output
244 /// iterator that the `formatContext` points to.
245 ///
246 /// \pre The behavior is undefined unless the requested format type is default and no precision is
247 /// specified. Notice that when a precision is specified the format used
248 /// changes to generic format (not "default").
249 template <class t_FORMAT_CONTEXT>
250 typename t_FORMAT_CONTEXT::iterator formatDefaultImpl(
251 t_VALUE value,
252 t_FORMAT_CONTEXT& formatContext,
253 const Specification& finalSpec) const;
254
255 /// Create the general string representation of the specified `value`,
256 /// customized in accordance with the requested format in the specified
257 /// `finalSpec`, and write the result to the output iterator that the `formatContext` points to.
258 ///
259 /// \pre The behavior is undefined unless the
260 /// requested format is general or uppercase general.
261 template <class t_FORMAT_CONTEXT>
262 typename t_FORMAT_CONTEXT::iterator formatGeneralImpl(
263 t_VALUE value,
264 t_FORMAT_CONTEXT& formatContext,
265 const Specification& finalSpec) const;
266
267 /// Create the minimal-round-tripping hexfloat string representation of the
268 /// specified `value`, customized in accordance with the requested format
269 /// in the specified `finalSpec`, and write the result to the output
270 /// iterator that the `formatContext` points to.
271 ///
272 /// \pre The behavior is undefined unless the requested format is hexfloat or uppercase hexfloat and no
273 /// precision is specified. Notice that there is a separate implementation
274 /// for hexfloats with a specified precision.
275 template <class t_FORMAT_CONTEXT>
276 typename t_FORMAT_CONTEXT::iterator formatHexImpl(
277 t_VALUE value,
278 t_FORMAT_CONTEXT& formatContext,
279 const Specification& finalSpec) const;
280
281 /// Create the hexfloat string representation of the specified `value`,
282 /// customized in accordance with the requested format in the specified
283 /// `finalSpec`, and write the result to the output iterator that the `formatContext` points to.
284 ///
285 /// \pre The behavior is undefined unless the
286 /// requested format is hexfloat or uppercase hexfloat and the required
287 /// precision is explicitly specified. Notice that there is a separate
288 /// implementation for hexfloats without a specified precision.
289 template <class t_FORMAT_CONTEXT>
290 typename t_FORMAT_CONTEXT::iterator formatHexPrecImpl(
291 t_VALUE value,
292 t_FORMAT_CONTEXT& formatContext,
293 const Specification& finalSpec) const;
294
295 /// Create the scientific string representation of the specified `value`,
296 /// customized in accordance with the requested format in the specified
297 /// `finalSpec`, and write the result to the output iterator that the `formatContext` points to.
298 ///
299 /// \pre The behavior is undefined unless the
300 /// requested format is scientific or uppercase scientific.
301 template <class t_FORMAT_CONTEXT>
302 typename t_FORMAT_CONTEXT::iterator formatScientificImpl(
303 t_VALUE value,
304 t_FORMAT_CONTEXT& formatContext,
305 const Specification& finalSpec) const;
306 public:
307 // MANIPULATORS
308
309 /// Create string representation of the specified `value`, customized in
310 /// accordance with the requested format and the specified `formatContext`,
311 /// and write the result to the output iterator that the `formatContext` points to.
312 ///
313 /// \pre The behavior is undefined unless `t_FORMAT_CONTEXT` is
314 /// either `std::format_context` if that is supported, or otherwise
315 /// `bslfmt::format_context`.
316 template <class t_FORMAT_CONTEXT>
317 typename t_FORMAT_CONTEXT::iterator format(
318 t_VALUE value,
319 t_FORMAT_CONTEXT& formatContext) const;
320
321 /// Parse the specified `parseContext`, store the resulting parsed
322 /// specification and return an iterator, pointing to the beginning of the format string.
323 ///
324 /// \pre The behavior is undefined unless `t_PARSE_CONTEXT` is
325 /// either `std::parse_context` if that is supported, or otherwise
326 /// `bslfmt::parse_context`.
327 template <class t_PARSE_CONTEXT>
328 BSLS_KEYWORD_CONSTEXPR_CPP20 typename t_PARSE_CONTEXT::iterator
329 parse(t_PARSE_CONTEXT& parseContext);
330};
331
332// ============================================================================
333// INLINE DEFINITIONS
334// ============================================================================
335
336 // -----------------------------
337 // struct FormatterFloating_Base
338 // -----------------------------
339
340// PRIVATE CLASS METHODS
341template <class t_VALUE, class t_CHAR>
342size_t
344 char *buf,
345 size_t numberLength)
346{
347 // Note that this method has to work with 3 substantially different-looking
348 // formats: scientific, fixed, and the special values (NaN, infinity).
349
350 {
351 const char lastChar = buf[numberLength - 1];
352 if ('f' == lastChar || 'n' == lastChar) {
353 // infinity or NaN, nothing to do
354 return numberLength;
355 }
356 }
357
358 size_t dotPos = k_NO_POS;
359 for (size_t i = 0; i < numberLength; ++i) {
360 if ('.' == buf[i]) {
361 dotPos = i;
362 break; // BREAK
363 }
364 }
365
366 if (dotPos == k_NO_POS) {
367 // When here, we need to insert the decimal point before the `e` if
368 // present, or at the end if we printed fixed-form.
369
370 size_t ePos = k_NO_POS;
371 for (size_t i = 0; i < numberLength; ++i) {
372 if ('e' == buf[i]) {
373 dotPos = i;
374 break; // BREAK
375 }
376 }
377
378 if (k_NO_POS == ePos) {
379 buf[numberLength] = '.';
380 }
381 else {
382 // Scientific form, we need to move the exponent part
383 memmove(buf + ePos + 1, buf + ePos, numberLength - ePos);
384 buf[ePos++] = '.';
385 }
386 ++numberLength;
387 }
388
389 return numberLength;
390}
391
392template <class t_VALUE, class t_CHAR>
393size_t
394FormatterFloating_Base<t_VALUE, t_CHAR>::applyFixedAlternate(
395 char *buf,
396 size_t numberLength)
397{
398 // Place in a decimal point if none is present
399
400 for (size_t i = 0; i < numberLength; ++i) {
401 if ('.' == buf[i]) {
402 // Has a decimal point, nothing to do
403 return numberLength; // RETURN
404 }
405 }
406
407 // When here, we need to insert the decimal point at the end
408 buf[numberLength] = '.';
409
410 return ++numberLength;
411}
412
413template <class t_VALUE, class t_CHAR>
414size_t
415FormatterFloating_Base<t_VALUE, t_CHAR>::applyGeneralAlternate(
416 char *buf,
417 size_t numberLength,
418 int precision)
419{
420 // This method adds a decimal point if it is not present and re-adds the
421 // removed trailing zeros up to the specified `precision` as required for
422 // the alternate format of the generic (`g` and `G`) formats. Note that
423 // this method has to work with 3 substantially different-looking formats:
424 // scientific, fixed, and the special value (NaN, infinity).
425
426 {
427 const char lastChar = buf[numberLength - 1];
428 if ('f' == lastChar || 'n' == lastChar) {
429 // infinity or NaN, nothing to do
430 return numberLength;
431 }
432 }
433
434 size_t dotPos = k_NO_POS;
435 for (size_t i = 0; i < numberLength; ++i) {
436 if ('.' == buf[i]) {
437 dotPos = i;
438 break; // BREAK
439 }
440 }
441 size_t ePos = k_NO_POS;
442 for (size_t i = 0; i < numberLength; ++i) {
443 if ('e' == buf[i]) {
444 ePos = i;
445 break; // BREAK
446 }
447 }
448
449 if (dotPos == k_NO_POS) {
450 // When here, we need to insert the decimal point before the `e` if
451 // present, or at the end if we printed fixed-form.
452
453 if (k_NO_POS == ePos) {
454 buf[numberLength] = '.';
455 }
456 else {
457 // Scientific form, we need to move the exponent part
458 memmove(buf + ePos + 1, buf + ePos, numberLength - ePos);
459 buf[ePos++] = '.';
460 }
461 ++numberLength;
462 }
463
464 // Alternate form also has to "put back" all removed trailing zeros up to
465 // the specified precision.
466 const ptrdiff_t digits = (k_NO_POS == ePos)
467 ? numberLength - 1
468 : ePos - 1;
469 if (precision > digits) {
470 const size_t numZeros = precision - digits;
471
472 // We need to put in some zeros
473 if (k_NO_POS == ePos) {
474 // Fixed format, zeros just go to the end
475 memset(buf + numberLength, '0', numZeros);
476 }
477 else {
478 // Scientific form, we need to move the exponent part
479 memmove(buf + ePos + numZeros,
480 buf + ePos,
481 numberLength - ePos);
482 memset(buf + ePos, '0', numZeros);
483 ePos += numZeros;
484 }
485 numberLength += numZeros;
486 }
487
488 return numberLength;
489}
490
491template <class t_VALUE, class t_CHAR>
492size_t
493FormatterFloating_Base<t_VALUE, t_CHAR>::applyScientificAlternate(
494 char expChar,
495 char *buf,
496 size_t numberLength)
497{
498 {
499 const char lastChar = buf[numberLength - 1];
500 if ('f' == lastChar || 'n' == lastChar) {
501 // infinity or NaN, nothing to do
502 return numberLength; // RETURN
503 }
504 }
505
506 for (size_t i = 0; i < numberLength; ++i) {
507 if ('.' == buf[i]) {
508 return numberLength; // RETURN
509 }
510 }
511
512 // When here, we need to insert the decimal point before the exponent
513 // character.
514
515 size_t expPos = k_NO_POS;
516 for (size_t i = 0; i < numberLength; ++i) {
517 if (expChar == buf[i]) {
518 expPos = i;
519 break; // BREAK
520 }
521 }
522
523 if (k_NO_POS == expPos) {
524 // This must not happen as we have a number in scientific format
525 BSLS_THROW(bsl::format_error("Internal error, exponent not found"));
526 }
527
528 // Scientific form, we need to move the exponent part
529 memmove(buf + expPos + 1, buf + expPos, numberLength - expPos);
530 buf[expPos] = '.';
531
532 return ++numberLength; // We added a '.'
533}
534
535
536// PRIVATE MANIPULATORS
537template <class t_VALUE, class t_CHAR>
538template <class t_FORMAT_CONTEXT>
539typename t_FORMAT_CONTEXT::iterator
540FormatterFloating_Base<t_VALUE, t_CHAR>::alignAndCopy(
541 const char *numberBuffer,
542 size_t numberLength,
543 t_FORMAT_CONTEXT& formatContext,
544 const Specification& finalSpec) const
545{
546 typedef FormatterSpecificationNumericValue NumericValue;
547 typedef bsl::basic_string_view<t_CHAR> StringView;
548 typedef PadUtil<t_CHAR> PadUtil;
549
550 NumericValue finalWidth(finalSpec.postprocessedWidth());
551
552 std::ptrdiff_t leftPadFillerCopiesNum = 0;
553 std::ptrdiff_t rightPadFillerCopiesNum = 0;
554 std::ptrdiff_t zeroPadFillerCopiesNum = 0;
555
556 const char addSignChar = *numberBuffer == '-'
557 ? '-'
558 : Specification::e_SIGN_POSITIVE == finalSpec.sign()
559 ? '+'
560 : Specification::e_SIGN_SPACE == finalSpec.sign()
561 ? ' '
562 : '\0';
563 const bool hasSignChar = (addSignChar != 0);
564
565 if ('-' == addSignChar) {
566 // We are going to add the sign "by hand"
567 ++numberBuffer;
568 --numberLength;
569 }
570
571 // Check if we have a non-numerical value of "inf" or "nan", in which case
572 // we must no use zero padding.
573 const char lastChar = numberBuffer[numberLength - 1];
574 const bool specialValue = 'f' == lastChar || 'n' == lastChar;
575
576 if (finalWidth.category() != NumericValue::e_DEFAULT &&
577 numberLength + hasSignChar < static_cast<size_t>(finalWidth.value())) {
578 // We need to fill the remaining space.
579
580 if (!specialValue &&
581 Specification::e_ALIGN_DEFAULT == finalSpec.alignment() &&
582 finalSpec.zeroPaddingFlag()) {
583 // Space will be filled with zeros.
584
585 zeroPadFillerCopiesNum = finalWidth.value() -
586 (numberLength + hasSignChar);
587 }
588 else {
589 // Alignment with appropriate symbol is required.
590
591 PadUtil::computePadding(&leftPadFillerCopiesNum,
592 &rightPadFillerCopiesNum,
593 finalWidth,
594 numberLength + hasSignChar,
595 d_spec.alignment(),
596 Specification::e_ALIGN_RIGHT);
597 }
598 }
599
600 // Assembling the final string.
601
602 typename t_FORMAT_CONTEXT::iterator outIterator = formatContext.out();
603
604 const StringView pad(finalSpec.filler(), finalSpec.numFillerCharacters());
605 outIterator = PadUtil::pad(outIterator, leftPadFillerCopiesNum, pad);
606
607 if (addSignChar) {
608 outIterator = FormatterCharUtil<t_CHAR>::outputFromChar(
609 addSignChar, outIterator);
610 }
611
612 outIterator = PadUtil::pad(outIterator, zeroPadFillerCopiesNum, '0');
613
614 outIterator = FormatterCharUtil<t_CHAR>::outputFromChar(
615 numberBuffer,
616 numberBuffer + numberLength,
617 outIterator);
618
619 outIterator = PadUtil::pad(outIterator, rightPadFillerCopiesNum, pad);
620
621 return outIterator;
622}
623
624template <class t_VALUE, class t_CHAR>
625template <class t_FORMAT_CONTEXT>
626typename t_FORMAT_CONTEXT::iterator
627FormatterFloating_Base<t_VALUE, t_CHAR>::formatFixedImpl(
628 t_VALUE value,
629 t_FORMAT_CONTEXT& formatContext,
630 const Specification& finalSpec) const
631{
632 typedef BloombergLP::bslalg::NumericFormatterUtil NFUtil;
633
634 /// Must be decimal format
635 BSLS_ASSERT(Specification::e_FLOATING_FIXED == finalSpec.formatType() ||
636 Specification::e_FLOATING_FIXED_UC == finalSpec.formatType());
637
638 // Determine the precision
639 typedef FormatterSpecificationNumericValue NumericValue;
640 const NumericValue& specPrec = finalSpec.postprocessedPrecision();
641 const int precision = (NumericValue::e_DEFAULT == specPrec.category())
642 ? 6
643 : specPrec.value();
644
645 // Converting to string
646 const size_t k_STACK_BUF_LEN = // Max useful precision 17 - dflt 6 => 11
647 NFUtil::ToCharsMaxLength<t_VALUE, NFUtil::e_FIXED>::k_VALUE + 11;
648 char sbuf[k_STACK_BUF_LEN];
649 char *dbuf = 0;
650
651 size_t bufLen = k_STACK_BUF_LEN;
652 char *buf = sbuf;
653
654 const size_t reqdBufLen =
655 NFUtil::PrecisionMaxBufferLength<t_VALUE>::value(NFUtil::e_FIXED,
656 precision);
658 if (reqdBufLen > k_STACK_BUF_LEN) {
659 dbuf = allocator.allocate(reqdBufLen);
660 buf = dbuf;
661 bufLen = reqdBufLen;
662 }
664 allocator,
665 dbuf,
666 dbuf ? reqdBufLen : 0);
667 char *end = NFUtil::toChars(buf,
668 buf + bufLen,
669 value,
670 NFUtil::e_FIXED,
671 precision);
672
673 size_t numberLength = static_cast<size_t>(end - buf);
674
675 if (finalSpec.alternativeFlag()) {
676 numberLength = applyFixedAlternate(buf, numberLength);
677 }
678
679 return alignAndCopy(buf, numberLength, formatContext, finalSpec);
680}
681
682template <class t_VALUE, class t_CHAR>
683template <class t_FORMAT_CONTEXT>
684typename t_FORMAT_CONTEXT::iterator
685FormatterFloating_Base<t_VALUE, t_CHAR>::formatDefaultImpl(
686 t_VALUE value,
687 t_FORMAT_CONTEXT& formatContext,
688 const Specification& finalSpec) const
689{
690 typedef BloombergLP::bslalg::NumericFormatterUtil NFUtil;
691
692 /// Must be default format
693 BSLS_ASSERT(Specification::e_FLOATING_DEFAULT == finalSpec.formatType());
694
695 /// Must not have precision
696 BSLS_ASSERT(finalSpec.postprocessedPrecision().category() ==
698
699 // Converting to string
700 char buf[NFUtil::ToCharsMaxLength<t_VALUE>::k_VALUE];
701 char *end = NFUtil::toChars(buf,
702 buf + sizeof(buf),
703 value);
704
705 size_t numberLength = static_cast<size_t>(end - buf);
706
707 if (finalSpec.alternativeFlag()) {
708 numberLength = applyDefaultAlternate(buf, numberLength);
709 }
710
711 return alignAndCopy(buf, numberLength, formatContext, finalSpec);
712}
713
714template <class t_VALUE, class t_CHAR>
715template <class t_FORMAT_CONTEXT>
716typename t_FORMAT_CONTEXT::iterator
717FormatterFloating_Base<t_VALUE, t_CHAR>::formatGeneralImpl(
718 t_VALUE value,
719 t_FORMAT_CONTEXT& formatContext,
720 const Specification& finalSpec) const
721{
722 typedef BloombergLP::bslalg::NumericFormatterUtil NFUtil;
723
724 /// Must be general format
726 Specification::e_FLOATING_GENERAL == finalSpec.formatType() ||
727 Specification::e_FLOATING_GENERAL_UC == finalSpec.formatType() ||
728 Specification::e_FLOATING_DEFAULT == finalSpec.formatType());
729
730 // Determine the precision
731 typedef FormatterSpecificationNumericValue FSNVAlue;
732 const FSNVAlue& specPrec = finalSpec.postprocessedPrecision();
733 const int precision = (FSNVAlue::e_DEFAULT == specPrec.category())
734 ? 6
735 : specPrec.value();
736
737 // Converting to string
738 const size_t k_STACK_BUF_LEN = // max useful prec 17 - deflt 6 => 11
739 NFUtil::ToCharsMaxLength<t_VALUE, NFUtil::e_GENERAL>::k_VALUE + 11;
740 char sbuf[k_STACK_BUF_LEN];
741 char *dbuf = 0;
742
743 size_t bufLen = k_STACK_BUF_LEN;
744 char *buf = sbuf;
745
746 const size_t reqdBufLen =
747 NFUtil::PrecisionMaxBufferLength<t_VALUE>::value(NFUtil::e_GENERAL,
748 precision);
750 if (reqdBufLen > k_STACK_BUF_LEN) {
751 dbuf = allocator.allocate(reqdBufLen);
752 buf = dbuf;
753 bufLen = reqdBufLen;
754 }
756 allocator,
757 dbuf,
758 dbuf ? reqdBufLen : 0);
759 char *end = NFUtil::toChars(buf,
760 buf + bufLen,
761 value,
762 NFUtil::e_GENERAL,
763 precision);
764
765 size_t numberLength = static_cast<size_t>(end - buf);
766
767 if (finalSpec.alternativeFlag()) {
768 numberLength = applyGeneralAlternate(buf, numberLength, precision);
769 }
770
771 if (Specification::e_FLOATING_GENERAL_UC == finalSpec.formatType()) {
772 BloombergLP::bslfmt::FormatterCharUtil<char>::toUpper(buf, end);
773 }
774
775 return alignAndCopy(buf, numberLength, formatContext, finalSpec);
776}
777
778template <class t_VALUE, class t_CHAR>
779template <class t_FORMAT_CONTEXT>
780typename t_FORMAT_CONTEXT::iterator
781FormatterFloating_Base<t_VALUE, t_CHAR>::formatHexImpl(
782 t_VALUE value,
783 t_FORMAT_CONTEXT& formatContext,
784 const Specification& finalSpec) const
785{
786 typedef BloombergLP::bslalg::NumericFormatterUtil NFUtil;
787
788 /// Must be hexfloat format
789 BSLS_ASSERT(Specification::e_FLOATING_HEX == finalSpec.formatType() ||
790 Specification::e_FLOATING_HEX_UC == finalSpec.formatType());
791
792 /// Must not have precision
793 BSLS_ASSERT(finalSpec.postprocessedPrecision().category() ==
795
796 // Converting to string
797 char buf[NFUtil::ToCharsMaxLength<t_VALUE, NFUtil::e_HEX>::k_VALUE];
798 char *end = NFUtil::toChars(buf,
799 buf + sizeof(buf),
800 value,
801 NFUtil::e_HEX);
802
803 size_t numberLength = static_cast<size_t>(end - buf);
804
805 if (finalSpec.alternativeFlag()) {
806 numberLength = applyScientificAlternate('p', buf, numberLength);
807 }
808
809 if (Specification::e_FLOATING_HEX_UC == finalSpec.formatType()) {
810 BloombergLP::bslfmt::FormatterCharUtil<char>::toUpper(buf, end);
811 }
812
813 return alignAndCopy(buf, numberLength, formatContext, finalSpec);
814}
815
816template <class t_VALUE, class t_CHAR>
817template <class t_FORMAT_CONTEXT>
818typename t_FORMAT_CONTEXT::iterator
819FormatterFloating_Base<t_VALUE, t_CHAR>::formatHexPrecImpl(
820 t_VALUE value,
821 t_FORMAT_CONTEXT& formatContext,
822 const Specification& finalSpec) const
823{
824 typedef BloombergLP::bslalg::NumericFormatterUtil NFUtil;
825
826 /// Must be hexfloat format
827 BSLS_ASSERT(Specification::e_FLOATING_HEX == finalSpec.formatType() ||
828 Specification::e_FLOATING_HEX_UC == finalSpec.formatType());
829
830 /// Must have precision
831 BSLS_ASSERT(finalSpec.postprocessedPrecision().category() !=
833
834 // Converting to string
835 const size_t k_STACK_BUF_LEN =
836 NFUtil::ToCharsMaxLength<t_VALUE, NFUtil::e_HEX>::k_VALUE;
837 char sbuf[k_STACK_BUF_LEN];
838 char *dbuf = 0;
839
840 size_t bufLen = k_STACK_BUF_LEN;
841 char *buf = sbuf;
842
843 const size_t reqdBufLen =
844 NFUtil::PrecisionMaxBufferLength<t_VALUE>::value(
845 NFUtil::e_HEX,
846 finalSpec.postprocessedPrecision().value());
848 if (reqdBufLen > k_STACK_BUF_LEN) {
849 dbuf = allocator.allocate(reqdBufLen);
850 buf = dbuf;
851 bufLen = reqdBufLen;
852 }
854 allocator,
855 dbuf,
856 dbuf ? reqdBufLen : 0);
857 char *end = NFUtil::toChars(buf,
858 buf + bufLen,
859 value,
860 NFUtil::e_HEX,
861 finalSpec.postprocessedPrecision().value());
862
863 size_t numberLength = static_cast<size_t>(end - buf);
864
865 if (finalSpec.alternativeFlag()) {
866 numberLength = applyScientificAlternate('p', buf, numberLength);
867 }
868
869 if (Specification::e_FLOATING_HEX_UC == finalSpec.formatType()) {
870 BloombergLP::bslfmt::FormatterCharUtil<char>::toUpper(buf, end);
871 }
872
873 return alignAndCopy(buf, numberLength, formatContext, finalSpec);
874}
875
876template <class t_VALUE, class t_CHAR>
877template <class t_FORMAT_CONTEXT>
878typename t_FORMAT_CONTEXT::iterator
879FormatterFloating_Base<t_VALUE, t_CHAR>::formatScientificImpl(
880 t_VALUE value,
881 t_FORMAT_CONTEXT& formatContext,
882 const Specification& finalSpec) const
883{
884 typedef BloombergLP::bslalg::NumericFormatterUtil NFUtil;
885
886 /// Must be scientific format
888 Specification::e_FLOATING_SCIENTIFIC == finalSpec.formatType() ||
889 Specification::e_FLOATING_SCIENTIFIC_UC == finalSpec.formatType());
890
891 // Determine the precision
892 typedef FormatterSpecificationNumericValue FSNVAlue;
893 const FSNVAlue& specPrec = finalSpec.postprocessedPrecision();
894 const int precision = (FSNVAlue::e_DEFAULT == specPrec.category())
895 ? 6
896 : specPrec.value();
897
898 // Converting to string
899 const size_t k_STACK_BUF_LEN = // max useful prec 17 - deflt 6 => 11
900 NFUtil::ToCharsMaxLength<t_VALUE, NFUtil::e_SCIENTIFIC>::k_VALUE + 11;
901 char sbuf[k_STACK_BUF_LEN];
902 char *dbuf = 0;
903
904 size_t bufLen = k_STACK_BUF_LEN;
905 char *buf = sbuf;
906
907 const size_t reqdBufLen =
908 NFUtil::PrecisionMaxBufferLength<t_VALUE>::value(NFUtil::e_SCIENTIFIC,
909 precision);
911 if (reqdBufLen > k_STACK_BUF_LEN) {
912 dbuf = allocator.allocate(reqdBufLen);
913 buf = dbuf;
914 bufLen = reqdBufLen;
915 }
917 allocator,
918 dbuf,
919 dbuf ? reqdBufLen : 0);
920 char *end = NFUtil::toChars(buf,
921 buf + bufLen,
922 value,
923 NFUtil::e_SCIENTIFIC,
924 precision);
925
926 size_t numberLength = static_cast<size_t>(end - buf);
927
928 if (finalSpec.alternativeFlag()) {
929 numberLength = applyScientificAlternate('e', buf, numberLength);
930 }
931
932 if (Specification::e_FLOATING_SCIENTIFIC_UC == finalSpec.formatType()) {
933 BloombergLP::bslfmt::FormatterCharUtil<char>::toUpper(buf, end);
934 }
935
936 return alignAndCopy(buf, numberLength, formatContext, finalSpec);
937}
938
939// MANIPULATORS
940template <class t_VALUE, class t_CHAR>
941template <class t_FORMAT_CONTEXT>
942typename t_FORMAT_CONTEXT::iterator
944 t_VALUE value,
945 t_FORMAT_CONTEXT& formatContext) const
946{
947 typedef FormatterSpecificationNumericValue FSNVAlue;
948
949 Specification finalSpec(d_spec);
950 finalSpec.postprocess(formatContext);
951
952 const bool isDefaultPrecision =
953 FSNVAlue::e_DEFAULT == finalSpec.postprocessedPrecision().category();
954
955 switch (finalSpec.formatType()) {
956 case Specification::e_FLOATING_DEFAULT: {
957 if (isDefaultPrecision) {
958 return formatDefaultImpl(value, formatContext, finalSpec);
959 }
960 else {
961 return formatGeneralImpl(value, formatContext, finalSpec);
962 }
963 } break;
964
965 case Specification::e_FLOATING_HEX: BSLA_FALLTHROUGH;
966 case Specification::e_FLOATING_HEX_UC: {
967 if (isDefaultPrecision) {
968 return formatHexImpl(value, formatContext, finalSpec);
969 }
970 else {
971 return formatHexPrecImpl(value, formatContext, finalSpec);
972 }
973 } break;
974
975 case Specification::e_FLOATING_SCIENTIFIC: BSLA_FALLTHROUGH;
976 case Specification::e_FLOATING_SCIENTIFIC_UC: {
977 return formatScientificImpl(value, formatContext, finalSpec);
978 } break;
979
980 case Specification::e_FLOATING_FIXED: BSLA_FALLTHROUGH;
981 case Specification::e_FLOATING_FIXED_UC: {
982 return formatFixedImpl(value, formatContext, finalSpec);
983 } break;
984
985 case Specification::e_FLOATING_GENERAL: BSLA_FALLTHROUGH;
986 case Specification::e_FLOATING_GENERAL_UC: {
987 return formatGeneralImpl(value, formatContext, finalSpec);
988 } break;
989
990 default: {
991 BSLS_THROW(bsl::format_error("Unknown format"));
992 }
993 }
994}
995
996template <class t_VALUE, class t_CHAR>
997template <class t_PARSE_CONTEXT>
998BSLS_KEYWORD_CONSTEXPR_CPP20 typename t_PARSE_CONTEXT::iterator
1000{
1001 d_spec.parse(&parseContext, Specification::e_CATEGORY_FLOATING);
1002
1003 if (d_spec.localeSpecificFlag()) {
1004 BSLS_THROW(bsl::format_error(
1005 "Formatting with the L specifier is not supported"));
1006 }
1007
1008 return parseContext.begin();
1009}
1010
1011} // close package namespace
1012
1013
1014namespace bsl {
1015// FORMATTER SPECIALIZATIONS
1016
1017/// This template partial specialization defines `bsl::formatter` for `float`
1018/// values for both (`char` and `wchar_t`) character types.
1019template <class t_CHAR>
1020struct formatter<float, t_CHAR>
1021: BloombergLP::bslfmt::FormatterFloating_Base<float, t_CHAR> {
1022};
1023
1024/// This template partial specialization defines `bsl::formatter` for `float`
1025/// values for both (`char` and `wchar_t`) character types.
1026template <class t_CHAR>
1027struct formatter<double, t_CHAR>
1028: BloombergLP::bslfmt::FormatterFloating_Base<double, t_CHAR> {
1029};
1030
1031/// This template partial specialization defines a disabled `bsl::formatter`
1032/// for `long double` values for both (`char` and `wchar_t`) character types.
1033///
1034/// \note Note that `long double` is disabled because we do not have a performant
1035/// implementation for this type, and it is also rarely used because it
1036/// essentially does not exist on MSVC because it is the same size and layout
1037/// as `double`. Plan is to implement it only in case it is actually needed.
1038template <class t_CHAR>
1039struct formatter<long double, t_CHAR> {
1040 template <class t_PARSE_CONTEXT>
1041 BSLS_KEYWORD_CONSTEXPR_CPP20 typename t_PARSE_CONTEXT::iterator parse(
1042 t_PARSE_CONTEXT& parseContext)
1043 {
1044 (void)parseContext;
1045 BSLS_THROW(bsl::format_error(
1046 "Formatting `long double` is not implemented yet"));
1047 }
1048
1049 template <class t_FORMAT_CONTEXT>
1050 typename t_FORMAT_CONTEXT::iterator format(
1051 long double value,
1052 t_FORMAT_CONTEXT& formatContext) const
1053 {
1054 (void)value;
1055 (void)formatContext;
1056 BSLS_THROW(bsl::format_error(
1057 "Formatting `long double` is not implemented yet"));
1058 }
1059};
1060
1061} // close namespace bsl
1062
1063#endif // INCLUDED_BSLFMT_FORMATTERFLOATING
1064
1065// ----------------------------------------------------------------------------
1066// Copyright 2023 Bloomberg Finance L.P.
1067//
1068// Licensed under the Apache License, Version 2.0 (the "License");
1069// you may not use this file except in compliance with the License.
1070// You may obtain a copy of the License at
1071//
1072// http://www.apache.org/licenses/LICENSE-2.0
1073//
1074// Unless required by applicable law or agreed to in writing, software
1075// distributed under the License is distributed on an "AS IS" BASIS,
1076// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1077// See the License for the specific language governing permissions and
1078// limitations under the License.
1079// ----------------------------- END-OF-FILE ----------------------------------
1080
1081/** @} */
1082/** @} */
1083/** @} */
Definition bslstl_stringview.h:471
Definition bslma_polymorphicallocator.h:460
BSLS_ANNOTATION_NODISCARD TYPE * allocate(std::size_t n)
Definition bslma_polymorphicallocator.h:963
Definition bslfmt_standardformatspecification.h:78
void postprocess(const t_FORMAT_CONTEXT &context)
Definition bslfmt_standardformatspecification.h:615
BSLS_KEYWORD_CONSTEXPR_CPP20 const FormatterSpecificationNumericValue postprocessedPrecision() const
Definition bslfmt_standardformatspecification.h:730
BSLS_KEYWORD_CONSTEXPR_CPP20 FormatType formatType() const
Definition bslfmt_standardformatspecification.h:745
Definition bslma_deallocatebytesproctor.h:272
#define BSLA_FALLTHROUGH
Definition bsla_fallthrough.h:188
#define BSLS_ASSERT(X)
Definition bsls_assert.h:1976
#define BSLS_THROW(X)
Definition bsls_exceptionutil.h:374
#define BSLS_IDENT(str)
BSLS_IDENT() - insert string into .comment binary segment (if supported)
Definition bsls_ident.h:238
#define BSLS_KEYWORD_CONSTEXPR_CPP20
Definition bsls_keyword.h:645
Definition bdlat_valuetypefunctions.h:939
T::iterator end(T &container)
Definition bslstl_iterator.h:1621
Definition bslfmt_enablestreamedformatter.h:130
BSLS_KEYWORD_CONSTEXPR_CPP20 t_PARSE_CONTEXT::iterator parse(t_PARSE_CONTEXT &parseContext)
Definition bslfmt_formatterfloating.h:1041
t_FORMAT_CONTEXT::iterator format(long double value, t_FORMAT_CONTEXT &formatContext) const
Definition bslfmt_formatterfloating.h:1050
Definition bslfmt_formatterbase.h:426
Definition bslfmt_formatterfloating.h:151
t_FORMAT_CONTEXT::iterator format(t_VALUE value, t_FORMAT_CONTEXT &formatContext) const
Definition bslfmt_formatterfloating.h:943
BSLS_KEYWORD_CONSTEXPR_CPP20 t_PARSE_CONTEXT::iterator parse(t_PARSE_CONTEXT &parseContext)
Definition bslfmt_formatterfloating.h:999
Definition bslfmt_formatterspecificationnumericvalue.h:100
@ e_DEFAULT
Definition bslfmt_formatterspecificationnumericvalue.h:104
BSLS_KEYWORD_CONSTEXPR_CPP20 Category category() const
Return the category attribute of this object.
Definition bslfmt_formatterspecificationnumericvalue.h:416
static void computePadding(std::ptrdiff_t *leftPadding, std::ptrdiff_t *rightPadding, const NumericValue &widthValue, std::ptrdiff_t contentWidth, Alignment alignment, Alignment defaultAlign=Enums::e_ALIGN_LEFT)
Definition bslfmt_padutil.h:352
static t_ITERATOR pad(t_ITERATOR out, std::ptrdiff_t padWidth, const bsl::string_view &filler)
Definition bslfmt_padutil.h:401