xref: /reactos/sdk/lib/ucrt/convert/cvt.cpp (revision e3e520d1)
1 //
2 // cvt.cpp
3 //
4 //      Copyright (c) Microsoft Corporation. All rights reserved.
5 //
6 // Functions for formatting floating point values with the %a, %e, %f, and %g
7 // printf format specifiers.
8 //
9 #include <corecrt_internal.h>
10 #include <corecrt_internal_ptd_propagation.h>
11 #include <ctype.h>
12 #include <string.h>
13 #include <math.h>
14 #include <locale.h>
15 #include <corecrt_internal_fltintrn.h>
16 #include <fenv.h>
17 #include <float.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <stddef.h>
21 
22 // The C String pointed to by string is shifted distance bytes to the right.
23 // If distance is negative, the string is shifted to the left.
24 // The C String pointed to by string and all shifting operations must be
25 // contained within buffer_base or buffer_count.
26 static void __cdecl shift_bytes(
27     _Maybe_unsafe_(_Inout_updates_z_, buffer_count)          char * const buffer_base,
28     _In_                                                     size_t const buffer_count,
29     _In_range_(buffer_base, buffer_base + buffer_count)      char*  const string,
30     _In_                                                     int    const distance
31     ) throw()
32 {
33     UNREFERENCED_PARAMETER(buffer_base);
34     UNREFERENCED_PARAMETER(buffer_count);
35 
36     if (distance != 0)
37     {
38         memmove(string + distance, string, strlen(string) + 1);
39     }
40 }
41 
42 
43 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
44 //
45 // NaN and Infinity Formatting
46 //
47 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
48 _Success_(return == 0)
49 static errno_t __cdecl fp_format_nan_or_infinity(
50     _In_                                                __acrt_fp_class const classification,
51     _In_range_(0,1)                                     bool            const is_negative,
52     _Maybe_unsafe_(_Out_writes_z_, result_buffer_count) char*                 result_buffer,
53     _In_range_(1,SIZE_MAX)                              size_t                result_buffer_count,
54     _In_range_(0,1)                                     bool            const use_capitals
55     ) throw()
56 {
57     using floating_traits = __acrt_floating_type_traits<double>;
58     using components_type = floating_traits::components_type;
59 
60     // Ensure that we have sufficient space to store at least the basic three-
61     // character INF or NAN string, plus the minus sign if required:
62     if (result_buffer_count < _countof("INF") + is_negative)
63     {
64         *result_buffer = '\0';
65         return ENOMEM;
66     }
67 
68     if (is_negative)
69     {
70         *result_buffer++ = '-';
71         *result_buffer   = '\0';
72         if (result_buffer_count != _CRT_UNBOUNDED_BUFFER_SIZE)
73         {
74             --result_buffer_count;
75         }
76     }
77 
78     static char const* const strings[][4] =
79     {
80         { "INF",       "INF", "inf",       "inf" }, // Infinity
81         { "NAN",       "NAN", "nan",       "nan" }, // Quiet NAN
82         { "NAN(SNAN)", "NAN", "nan(snan)", "nan" }, // Signaling NAN
83         { "NAN(IND)",  "NAN", "nan(ind)",  "nan" }, // Indeterminate
84     };
85 
86     uint32_t const row    = static_cast<uint32_t>(classification) - 1;
87     uint32_t const column = use_capitals ? 0 : 2;
88 
89     bool const long_string_will_fit = result_buffer_count > strlen(strings[row][column]);
90     _ERRCHECK(strcpy_s(
91         result_buffer,
92         result_buffer_count,
93         strings[row][column + !long_string_will_fit]));
94     return 0;
95 }
96 
97 
98 
99 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
100 //
101 // %e formatting
102 //
103 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
104 // These functions handle the formatting of floating point values in the %e
105 // printf format.  This format has the form [-]d.ddde(+/-)ddd, where there will
106 // be 'precision' digits following the decimal point.  If the precision is less
107 // than or equal to zero, no decimal point will appear.  The low order digit is
108 // rounded.  If 'capitals' is true, then the exponent will appear as E(+/-)ddd.
109 _Success_(return == 0)
110 static errno_t fp_format_e_internal(
111     _Maybe_unsafe_(_Inout_updates_z_, result_buffer_count) char*              const result_buffer,
112     _In_fits_precision_(precision)                         size_t             const result_buffer_count,
113     _In_                                                   int                const precision,
114     _In_                                                   bool               const capitals,
115     _In_                                                   unsigned           const min_exponent_digits,
116     _In_                                                   STRFLT             const pflt,
117     _In_                                                   bool               const g_fmt,
118     _Inout_                                                __crt_cached_ptd_host&   ptd
119     ) throw()
120 {
121     // The max length if calculated like this:
122     // 3 = sign + first digit + decimal point
123     // precision = decimal digits
124     // 5 = exponent letter (e or E), exponent sign, three digits exponent
125     // 1 = extra space for rounding
126     _UCRT_VALIDATE_RETURN_ERRCODE(ptd, result_buffer_count > static_cast<size_t>(3 + (precision > 0 ? precision : 0) + 5 + 1), ERANGE);
127 
128     // Place the output in the buffer and round.  Leave space in the buffer
129     // for the '-' sign (if any) and the decimal point (if any):
130     if (g_fmt)
131     {
132         // Shift it right one place if nec. for decimal point:
133         char* const p = result_buffer + (pflt->sign == '-');
134         shift_bytes(result_buffer, result_buffer_count, p, precision > 0);
135     }
136 
137     // Now fix the number up to be in e format:
138     char* p = result_buffer;
139 
140     // Put in negative sign if needed:
141     if (pflt->sign == '-')
142         *p++ = '-';
143 
144     // Put in decimal point if needed.  Copy the first digit to the place left
145     // for it and put the decimal point in its place:
146     if (precision > 0)
147     {
148         *p = *(p + 1);
149         *++p = *ptd.get_locale()->locinfo->lconv->decimal_point;
150     }
151 
152     // Find the end of the string, attach the exponent field and save the
153     // exponent position:
154     p = p + precision + (g_fmt ? 0 : 1);
155     _ERRCHECK(strcpy_s(
156         p,
157         result_buffer_count == _CRT_UNBOUNDED_BUFFER_SIZE
158             ? result_buffer_count
159             : result_buffer_count - (p - result_buffer), "e+000"));
160     char* exponentpos = p + 2;
161 
162     // Adjust exponent indicator according to capitals flag and increment
163     // pointer to point to exponent sign:
164     if (capitals)
165         *p = 'E';
166 
167     ++p;
168 
169     // If mantissa is zero, then the number is 0 and we are done; otherwise
170     // adjust the exponent sign (if necessary) and value:
171     if (*pflt->mantissa != '0')
172     {
173         // Check to see if exponent is negative; if so adjust exponent sign and
174         // exponent value:
175         int exp = pflt->decpt - 1;
176         if (exp < 0)
177         {
178             exp = -exp;
179             *p = '-';
180         }
181 
182         ++p;
183 
184         if (exp >= 100)
185         {
186             *p += static_cast<char>(exp / 100);
187             exp %= 100;
188         }
189 
190         ++p;
191 
192         if (exp >= 10)
193         {
194             *p += static_cast<char>(exp / 10);
195             exp %= 10;
196         }
197 
198         *++p += static_cast<char>(exp);
199     }
200 
201     if (min_exponent_digits == 2)
202     {
203         // If possible, reduce the exponent to two digits:
204         if (*exponentpos == '0')
205         {
206             memmove(exponentpos, exponentpos + 1, 3);
207         }
208     }
209 
210     return 0;
211 }
212 
213 _Success_(return == 0)
214 static errno_t __cdecl fp_format_e(
215     _In_                                                   double const*        const argument,
216     _Maybe_unsafe_(_Inout_updates_z_, result_buffer_count) char*                const result_buffer,
217     _In_fits_precision_(precision)                         size_t               const result_buffer_count,
218     _Out_writes_(scratch_buffer_count)                     char*                const scratch_buffer,
219     _In_                                                   size_t               const scratch_buffer_count,
220     _In_                                                   int                  const precision,
221     _In_                                                   bool                 const capitals,
222     _In_                                                   unsigned             const min_exponent_digits,
223     _In_                                                   __acrt_rounding_mode const rounding_mode,
224     _Inout_                                                __crt_cached_ptd_host&     ptd
225     ) throw()
226 {
227     // The precision passed to __acrt_fltout is the number of fractional digits.
228     // To ensure that we get enough digits, we require a total of precision + 1 digits,
229     // to account for the digit placed to the left of the decimal point when all digits are fractional.
230     _strflt strflt;
231 
232     __acrt_has_trailing_digits const trailing_digits = __acrt_fltout(
233         *reinterpret_cast<_CRT_DOUBLE const*>(argument),
234         precision + 1,
235         __acrt_precision_style::scientific,
236         &strflt,
237         scratch_buffer,
238         scratch_buffer_count
239         );
240 
241     errno_t const e = __acrt_fp_strflt_to_string(
242         result_buffer + (strflt.sign == '-') + (precision > 0),
243         (result_buffer_count == _CRT_UNBOUNDED_BUFFER_SIZE
244             ? result_buffer_count
245             : result_buffer_count - (strflt.sign == '-') - (precision > 0)),
246         precision + 1,
247         &strflt,
248         trailing_digits,
249         rounding_mode,
250         ptd);
251 
252     if (e != 0)
253     {
254         result_buffer[0] = '\0';
255         return e;
256     }
257 
258     return fp_format_e_internal(result_buffer, result_buffer_count, precision, capitals, min_exponent_digits, &strflt, false, ptd);
259 }
260 
261 static bool fe_to_nearest(double const* const argument, unsigned __int64 const mask, short const maskpos)
262 {
263     using floating_traits = __acrt_floating_type_traits<double>;
264     using components_type = floating_traits::components_type;
265     components_type const* const components = reinterpret_cast<components_type const*>(argument);
266 
267     unsigned short digit = static_cast<unsigned short>((components->_mantissa & mask) >> maskpos);
268 
269     if (digit > 8)
270     {
271         return true;
272     }
273 
274     if (digit < 8)
275     {
276         return false;
277     }
278 
279     unsigned __int64 const roundBitsMask = (static_cast<unsigned __int64>(1) << maskpos) - 1;
280     if (components->_mantissa & roundBitsMask)
281     {
282         return true;
283     }
284 
285     //if we still have digits to the left to compare
286     if (maskpos != DBL_MANT_DIG - 5)
287     {
288         // We divide the mantisa by 16 to move the digits to the right, after that we apply the mask
289         // to get the digit at the left.
290         digit = static_cast<unsigned short>(((components->_mantissa / 16) & mask) >> maskpos);
291     }
292     else
293     {
294         digit = components->_exponent == 0 ? 0 : 1;
295     }
296 
297     return digit % 2 == 1;
298 }
299 
300 static bool should_round_up(double const* const argument, unsigned __int64 const mask, short const maskpos, __acrt_rounding_mode const rounding_mode)
301 {
302     using floating_traits = __acrt_floating_type_traits<double>;
303     using components_type = floating_traits::components_type;
304     components_type const* const components = reinterpret_cast<components_type const*>(argument);
305 
306     unsigned short const digit = static_cast<unsigned short>((components->_mantissa & mask) >> maskpos);
307 
308     if (rounding_mode == __acrt_rounding_mode::legacy)
309     {
310         return digit >= 8;
311     }
312     int const round_mode = fegetround();
313 
314     if (round_mode == FE_TONEAREST)
315     {
316         return fe_to_nearest(argument, mask, maskpos);
317     }
318 
319     if (round_mode == FE_UPWARD)
320     {
321         return digit != 0 && !components->_sign;
322     }
323 
324     if (round_mode == FE_DOWNWARD)
325     {
326         return digit != 0 && components->_sign;
327     }
328 
329     return false;
330 }
331 
332 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
333 //
334 // %a formatting
335 //
336 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
337 // These functions handle the formatting of floating point values in the %a
338 // printf format.  This format has the form [-]0xh.hhhhp(+/-)d, where there will
339 // be 'precision' hex digits following the decimal point.  If the precision is
340 // less than or equal to zero, no decimal point will appear.  If 'capitals' is
341 // true, then the number will appear as [-]0xH.HHHHP(+/-)d.
342 _Success_(return == 0)
343 static errno_t __cdecl fp_format_a(
344     _In_                                                   double const*        const argument,
345     _Maybe_unsafe_(_Inout_updates_z_, result_buffer_count) char*                      result_buffer,
346     _In_fits_precision_(precision)                         size_t               const result_buffer_count,
347     _Out_writes_(scratch_buffer_count)                     char*                const scratch_buffer,
348     _In_                                                   size_t               const scratch_buffer_count,
349     _In_                                                   int                        precision,
350     _In_                                                   bool                 const capitals,
351     _In_                                                   unsigned             const min_exponent_digits,
352     _In_                                                   __acrt_rounding_mode const rounding_mode,
353     _Inout_                                                __crt_cached_ptd_host&     ptd
354     )
355 {
356     using floating_traits = __acrt_floating_type_traits<double>;
357     using components_type = floating_traits::components_type;
358 
359     if (precision < 0)
360     {
361         precision = 0;
362     }
363 
364     result_buffer[0] = '\0';
365 
366     // the constraint for the size of buffer is:
367     // 1 (sign)
368     // 4 ("0xh.")
369     // precision (decimal digits)
370     // 6 ("p+0000")
371     _UCRT_VALIDATE_RETURN_ERRCODE(ptd, result_buffer_count > static_cast<size_t>(1 + 4 + precision + 6), ERANGE);
372 
373     // Let __acrt_fp_format_e handle the special cases like SNAN, etc.:
374     components_type const* const components = reinterpret_cast<components_type const*>(argument);
375     if (components->_exponent == floating_traits::exponent_mask)
376     {
377         errno_t const e = fp_format_e(
378             argument,
379             result_buffer,
380             result_buffer_count,
381             scratch_buffer,
382             scratch_buffer_count,
383             precision,
384             false,
385             min_exponent_digits,
386             rounding_mode,
387             ptd);
388 
389         if (e != 0)
390         {
391             // An error occurred
392             result_buffer[0] = '\0';
393             return e;
394         }
395 
396         // Substitute the e with p:
397         char* p = strrchr(result_buffer, 'e');
398         if (p)
399         {
400             *p = capitals ? 'P' : 'p';
401 
402             // Trim the exponent (which is 0) to only one digit; skip the
403             // exponent sign and the first digit and put the terminating 0:
404             p += 3;
405             *p = 0;
406         }
407         return e;
408     }
409 
410     // Sign:
411     if (components->_sign)
412     {
413         *result_buffer++ = '-';
414     }
415 
416     int const hexadd = (capitals ? 'A' : 'a') - '9' - 1;
417 
418     // Leading digit (and set the debias):
419     unsigned __int64 debias = floating_traits::exponent_bias;
420     if (components->_exponent == 0)
421     {
422         *result_buffer++ = '0';
423         if (components->_mantissa == 0)
424         {
425             // Zero:
426             debias = 0;
427         }
428         else
429         {
430             // Denormal:
431             debias--;
432         }
433     }
434     else
435     {
436         *result_buffer++ = '1';
437     }
438 
439     // Decimal point (save the position in pos):
440     char* pos = result_buffer++;
441     if (precision == 0)
442     {
443         // If precision is 0, then we don't have to print the decimal point:
444         // we mark this putting 0 instead of the decimal point itself
445         *pos = 0;
446     }
447     else
448     {
449         *pos = *ptd.get_locale()->locinfo->lconv->decimal_point;
450     }
451 
452     // Mantissa:
453     if (components->_mantissa > 0)
454     {
455         // Print 4 bits at a time, and skip the initial zeroes
456         // Prepare the mask to read the first 4 bits
457         short maskpos = (floating_traits::mantissa_bits - 1) - 4;
458 
459         unsigned __int64 mask = 0xf;
460         mask <<= maskpos;
461 
462         while (maskpos >= 0 && precision > 0)
463         {
464             unsigned short digit = static_cast<unsigned short>((components->_mantissa & mask) >> maskpos);
465             digit += '0';
466             if (digit > '9')
467             {
468                 digit += static_cast<unsigned short>(hexadd);
469             }
470             *result_buffer++ = static_cast<char>(digit);
471             mask >>= 4;
472             maskpos -= 4;
473             --precision;
474         }
475 
476         // Round the mantissa:
477         if (maskpos >= 0)
478         {
479             if (should_round_up(argument, mask, maskpos, rounding_mode))
480             {
481                 char* p = result_buffer;
482                 --p;
483                 // If the last digit is 'f', we need to add one to the previous
484                 // digit, too; pos is the position of the decimal point
485                 while (*p == 'f' || *p == 'F')
486                 {
487                     *p-- = '0';
488                 }
489                 // If we reached the decimal point, it means we are rounding
490                 // something like 0x0.fffff so this will become 0x1.00000 :
491                 if (p != pos)
492                 {
493                     if (*p == '9')
494                     {
495                         *p += static_cast<char>(1 + hexadd);
496                     }
497                     else
498                     {
499                         *p += 1;
500                     }
501                 }
502                 else // p == pos
503                 {
504                     // Skip the decimal point:
505                     --p;
506 
507                     // The first digit is always 0 or 1, so we don't need to
508                     // add hexadd:
509                     *p += 1;
510                 }
511             }
512         }
513     }
514 
515     // Add the final zeroes, if needed:
516     for (; precision > 0; --precision)
517     {
518         *result_buffer++ = '0';
519     }
520 
521     // Move back the buffer pointer if there is no decimal point:
522     if (*pos == 0)
523     {
524         result_buffer = pos;
525     }
526 
527     // Exponent:
528     *result_buffer++ = capitals ? 'P' : 'p';
529     __int64 exponent = components->_exponent - debias;
530     if (exponent >= 0)
531     {
532         *result_buffer++ = '+';
533     }
534     else
535     {
536         *result_buffer++ = '-';
537         exponent = -exponent;
538     }
539     // Save the position in pos and write a '0':
540     pos = result_buffer;
541     *pos = '0';
542 
543     if (exponent >= 1000)
544     {
545         *result_buffer++ = '0' + static_cast<char>(exponent / 1000);
546         exponent %= 1000;
547     }
548     if (result_buffer != pos || exponent >= 100)
549     {
550         *result_buffer++ = '0' + static_cast<char>(exponent / 100);
551         exponent %= 100;
552     }
553     if (result_buffer != pos || exponent >= 10)
554     {
555         *result_buffer++ = '0' + static_cast<char>(exponent / 10);
556         exponent %= 10;
557     }
558 
559     *result_buffer++ = '0' + static_cast<char>(exponent);
560 
561     // Terminate the string:
562     *result_buffer = '\0';
563 
564     return 0;
565 }
566 
567 
568 
569 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
570 //
571 // %f formatting
572 //
573 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
574 // These functions handle the formatting of floating point values in the %f
575 // printf format.  This format has the form [-]ddddd.ddddd, where there will be
576 // precision digits following the decimal point.  If precision is less than or
577 // equal to zero, no decimal point will appear.  The low order digit is rounded.
578 _Success_(return == 0)
579 static errno_t fp_format_f_internal(
580     _Pre_z_ _Maybe_unsafe_(_Inout_updates_z_, buffer_count) char*              const buffer,
581     _In_fits_precision_(precision)                          size_t             const buffer_count,
582     _In_                                                    int                const precision,
583     _In_                                                    STRFLT             const pflt,
584     _In_                                                    bool               const g_fmt,
585     _Inout_                                                 __crt_cached_ptd_host&   ptd
586     ) throw()
587 {
588     int const g_magnitude = pflt->decpt - 1;
589 
590     // Place the output in the user's buffer and round.  Save space for the
591     // minus sign now if it will be needed:
592     if (g_fmt && g_magnitude == precision)
593     {
594         char* const p = g_magnitude + buffer + (pflt->sign == '-');
595         p[0] = '0';
596         p[1] = '\0';
597 
598         // Allows for extra place-holding '0' in the exponent == precision case
599         // of the %g format.
600     }
601 
602     // Now fix up the number to be in the correct %f format:
603     char* p = buffer;
604 
605     // Put in a negative sign, if necessary:
606     if (pflt->sign == '-')
607         *p++ = '-';
608 
609     // Insert a leading zero for purely fractional values and position ourselves
610     // at the correct spot for inserting the decimal point:
611     if (pflt->decpt <= 0)
612     {
613         // In the specific scenario of the 0 when using g format this would cause
614         // to have an extra zero at the end
615         bool const is_zero_pflt = pflt->decpt == 0 && *pflt->mantissa == '0';
616         if (!g_fmt || !is_zero_pflt) {
617             shift_bytes(buffer, buffer_count, p, 1);
618         }
619         *p++ = '0';
620     }
621     else
622     {
623         p += pflt->decpt;
624     }
625 
626     // Put a decimal point if required, and add any needed zero padding:
627     if (precision > 0)
628     {
629         shift_bytes(buffer, buffer_count, p, 1);
630         *p++ = *ptd.get_locale()->locinfo->lconv->decimal_point;
631 
632         // If the value is less than 1 then we may need to put zeroes out in
633         // front of the first non-zero digit of the mantissa:
634         if (pflt->decpt < 0)
635         {
636             int const computed_precision = (g_fmt || -pflt->decpt < precision)
637                 ? -pflt->decpt
638                 : precision;
639 
640             shift_bytes(buffer, buffer_count, p, computed_precision);
641             memset(p, '0', computed_precision);
642         }
643     }
644 
645     return 0;
646 }
647 
648 _Success_(return == 0)
649 static errno_t __cdecl fp_format_f(
650     _In_                                                   double const*        const argument,
651     _Maybe_unsafe_(_Inout_updates_z_, result_buffer_count) char*                const result_buffer,
652     _In_fits_precision_(precision)                         size_t               const result_buffer_count,
653     _Out_writes_(scratch_buffer_count)                     char*                const scratch_buffer,
654     _In_                                                   size_t               const scratch_buffer_count,
655     _In_                                                   int                  const precision,
656     _In_                                                   __acrt_rounding_mode const rounding_mode,
657     _Inout_                                                __crt_cached_ptd_host&     ptd
658     ) throw()
659 {
660     _strflt strflt{};
661     __acrt_has_trailing_digits const trailing_digits = __acrt_fltout(
662         *reinterpret_cast<_CRT_DOUBLE const*>(argument),
663         precision,
664         __acrt_precision_style::fixed,
665         &strflt,
666         scratch_buffer,
667         scratch_buffer_count
668         );
669 
670     errno_t const e = __acrt_fp_strflt_to_string(
671         result_buffer + (strflt.sign == '-'),
672         (result_buffer_count == _CRT_UNBOUNDED_BUFFER_SIZE ? result_buffer_count : result_buffer_count - (strflt.sign == '-')),
673         precision + strflt.decpt,
674         &strflt,
675         trailing_digits,
676         rounding_mode,
677         ptd);
678 
679     if (e != 0)
680     {
681         result_buffer[0] = '\0';
682         return e;
683     }
684 
685     return fp_format_f_internal(result_buffer, result_buffer_count, precision, &strflt, false, ptd);
686 }
687 
688 
689 
690 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
691 //
692 // %g formatting
693 //
694 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
695 // These functions handle the formatting of floating point values in the %g
696 // printf format.  The form used depends on the value converted.  The printf %e
697 // form will be used if the magnitude of the value is less than -4 or is greater
698 // than 'precision', otherwise %f will be used.  The 'precision' always specifies
699 // the number of digits following the decimal point.  The low order digit is
700 // appropriately rounded.
701 _Success_(return == 0)
702 static errno_t __cdecl fp_format_g(
703     _In_                                                   double const*        const argument,
704     _Maybe_unsafe_(_Inout_updates_z_, result_buffer_count) char*                const result_buffer,
705     _In_fits_precision_(precision)                         size_t               const result_buffer_count,
706     _Out_writes_(scratch_buffer_count)                     char*                const scratch_buffer,
707     _In_                                                   size_t               const scratch_buffer_count,
708     _In_                                                   int                  const precision,
709     _In_                                                   bool                 const capitals,
710     _In_                                                   unsigned             const min_exponent_digits,
711     _In_                                                   __acrt_rounding_mode const rounding_mode,
712     _Inout_                                                __crt_cached_ptd_host&   ptd
713     ) throw()
714 {
715     _strflt strflt{};
716 
717     // Generate digits as though we will use %f formatting, then decide based on the result
718     // whether to use %f or %e formatting. %f always requires more generated digits than %e,
719     // so generating them all now will avoid generating more later (generation isn't resumable).
720     __acrt_has_trailing_digits const trailing_digits = __acrt_fltout(
721         *reinterpret_cast<_CRT_DOUBLE const*>(argument),
722         precision,
723         __acrt_precision_style::fixed,
724         &strflt,
725         scratch_buffer,
726         scratch_buffer_count
727         );
728 
729     size_t const minus_sign_length = strflt.sign == '-' ? 1 : 0;
730 
731     int g_magnitude = strflt.decpt - 1;
732     char* p = result_buffer + minus_sign_length;
733 
734     size_t const buffer_count_for_fptostr = result_buffer_count == _CRT_UNBOUNDED_BUFFER_SIZE
735         ? result_buffer_count
736         : result_buffer_count - minus_sign_length;
737 
738     errno_t const fptostr_result = __acrt_fp_strflt_to_string(p, buffer_count_for_fptostr, precision, &strflt, trailing_digits, rounding_mode, ptd);
739     if (fptostr_result != 0)
740     {
741         result_buffer[0] = '\0';
742         return fptostr_result;
743     }
744 
745     bool const g_round_expansion = g_magnitude < (strflt.decpt - 1);
746 
747     // Compute the magnitude of value:
748     g_magnitude = strflt.decpt - 1;
749 
750     // Convert value to the C Language g format:
751     if (g_magnitude < -4 || g_magnitude >= precision) // Use e format
752     {
753         // We can ignore the round expansion flag here:  the extra digit will be
754         // overwritten by "e+xxx".
755         return fp_format_e_internal(result_buffer, result_buffer_count, precision, capitals, min_exponent_digits, &strflt, true, ptd);
756     }
757     else // Use f format
758     {
759         if (g_round_expansion)
760         {
761             // Throw away extra final digit from expansion:
762             while (*p++)
763             {
764                 // Iterate to the end of the string
765             }
766 
767             *(p - 2) = '\0';
768         }
769 
770         return fp_format_f_internal(result_buffer, result_buffer_count, precision, &strflt, true, ptd);
771     }
772 }
773 
774 
775 
776 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
777 //
778 // Format Dispatch
779 //
780 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
781 // The main floating point formatting dispatch function.  This function just
782 // looks at the 'format' character, calls the right formatting function, and
783 // returns the result.  The other parameters are passed on to the selected
784 // formatting function and are used as described in the documentation for
785 // those functions.
786 extern "C" errno_t __cdecl __acrt_fp_format(
787     double const*        const value,
788     char*                const result_buffer,
789     size_t               const result_buffer_count,
790     char*                const scratch_buffer,
791     size_t               const scratch_buffer_count,
792     int                  const format,
793     int                  const precision,
794     uint64_t             const options,
795     __acrt_rounding_mode       rounding_mode,
796     __crt_cached_ptd_host&     ptd
797     )
798 {
799     _UCRT_VALIDATE_RETURN_ERRCODE(ptd, result_buffer != nullptr,  EINVAL);
800     _UCRT_VALIDATE_RETURN_ERRCODE(ptd, result_buffer_count > 0,   EINVAL);
801     _UCRT_VALIDATE_RETURN_ERRCODE(ptd, scratch_buffer != nullptr, EINVAL);
802     _UCRT_VALIDATE_RETURN_ERRCODE(ptd, scratch_buffer_count > 0,  EINVAL);
803 
804     bool const use_capitals = format == 'A' || format == 'E' || format == 'F' || format == 'G';
805 
806     // Detect special cases (NaNs and infinities) and handle them specially.
807     // Note that the underlying __acrt_fltout function will also handle these
808     // special cases, but it does so using the legacy strings (e.g. 1.#INF).
809     // Our special handling here uses the C99 strings (e.g. INF).
810     if ((options & _CRT_INTERNAL_PRINTF_LEGACY_MSVCRT_COMPATIBILITY) == 0)
811     {
812         __acrt_fp_class const classification = __acrt_fp_classify(*value);
813         if (classification != __acrt_fp_class::finite)
814         {
815             return fp_format_nan_or_infinity(
816                 classification,
817                 __acrt_fp_is_negative(*value),
818                 result_buffer,
819                 result_buffer_count,
820                 use_capitals);
821         }
822     }
823 
824     unsigned const min_exponent_digits = (options & _CRT_INTERNAL_PRINTF_LEGACY_THREE_DIGIT_EXPONENTS) != 0 ? 3 : 2;
825     if ((options & _CRT_INTERNAL_PRINTF_STANDARD_ROUNDING) == 0) {
826         rounding_mode = __acrt_rounding_mode::legacy;
827     }
828 
829     switch (format)
830     {
831     case 'a':
832     case 'A':
833         return fp_format_a(value, result_buffer, result_buffer_count, scratch_buffer, scratch_buffer_count, precision, use_capitals, min_exponent_digits, rounding_mode, ptd);
834 
835     case 'e':
836     case 'E':
837         return fp_format_e(value, result_buffer, result_buffer_count, scratch_buffer, scratch_buffer_count, precision, use_capitals, min_exponent_digits, rounding_mode, ptd);
838 
839     case 'f':
840     case 'F':
841         return fp_format_f(value, result_buffer, result_buffer_count, scratch_buffer, scratch_buffer_count, precision, rounding_mode, ptd);
842 
843     default:
844         _ASSERTE(("Unsupported format specifier", 0));
845     case 'g':
846     case 'G':
847         return fp_format_g(value, result_buffer, result_buffer_count, scratch_buffer, scratch_buffer_count, precision, use_capitals, min_exponent_digits, rounding_mode, ptd);
848     }
849 }
850