xref: /reactos/sdk/lib/ucrt/convert/gcvt.cpp (revision e3e520d1)
1 //
2 // gcvt.cpp
3 //
4 //      Copyright (c) Microsoft Corporation. All rights reserved.
5 //
6 // Defines the _gcvt functions, which convert a floating point value to a narrow
7 // character string.  It attempts to produce 'precision' significant digits in
8 // the Fortran F format if possible, otherwise the E format.  Trailing zeroes may
9 // be suppressed.  The _s-suffixed function returns zero on success; an error
10 // code on failure.  If the buffer is too small, that is an error.
11 //
12 #include <corecrt_internal.h>
13 #include <corecrt_internal_fltintrn.h>
14 #include <corecrt_internal_ptd_propagation.h>
15 #include <corecrt_internal_securecrt.h>
16 #include <corecrt_stdio_config.h>
17 #include <locale.h>
18 #include <stdlib.h>
19 
20 
21 
22 static errno_t __cdecl _gcvt_s_internal(
23     char*              const buffer,
24     size_t             const buffer_count,
25     double             const value,
26     int                const precision,
27     __crt_cached_ptd_host&   ptd
28     )
29 {
30     _UCRT_VALIDATE_RETURN_ERRCODE(ptd, buffer != nullptr, EINVAL);
31     _UCRT_VALIDATE_RETURN_ERRCODE(ptd, buffer_count > 0,  EINVAL);
32     _RESET_STRING(buffer, buffer_count);
33     _UCRT_VALIDATE_RETURN_ERRCODE(ptd, static_cast<size_t>(precision) < buffer_count, ERANGE);
34     // Additional validation will be performed in the fp_format functions.
35 
36     char const decimal_point = *ptd.get_locale()->locinfo->lconv->decimal_point;
37 
38     // We only call __acrt_fltout in order to parse the correct exponent value (strflt.decpt).
39     // Therefore, we don't want to generate any digits, so we pass a buffer size only large
40     // enough to hold the inf, nan, or ind string to prevent failure.
41 
42     size_t const restricted_count = 7; // "1#SNAN" + 1 null terminator
43     char result_string[restricted_count];
44 
45     _strflt strflt{};
46 
47     __acrt_fltout(
48         reinterpret_cast<_CRT_DOUBLE const&>(value),
49         precision,
50         __acrt_precision_style::fixed,
51         &strflt,
52         result_string,
53         restricted_count);
54 
55     int const magnitude = strflt.decpt - 1;
56 
57     // Output the result according to the Fortran G format as outlined in the
58     // Fortran language specification.
59     if (magnitude < -1 || magnitude > precision - 1)
60     {
61         // Ew.d where d = precision
62         char scratch_buffer[_CVTBUFSIZE + 1];
63         errno_t const e = __acrt_fp_format(
64             &value,
65             buffer,
66             buffer_count,
67             scratch_buffer,
68             _countof(scratch_buffer),
69             'e',
70             precision - 1,
71             _CRT_INTERNAL_PRINTF_LEGACY_THREE_DIGIT_EXPONENTS,
72             __acrt_rounding_mode::legacy,
73             ptd);
74 
75         if (e != 0)
76         {
77             return ptd.get_errno().set(e);
78         }
79     }
80     else
81     {
82         // Fw.d where d = precision-string->decpt
83         char scratch_buffer[_CVTBUFSIZE + 1];
84         errno_t const e = __acrt_fp_format(
85             &value,
86             buffer,
87             buffer_count,
88             scratch_buffer,
89             _countof(scratch_buffer),
90             'f',
91             precision - strflt.decpt,
92             _CRT_INTERNAL_PRINTF_LEGACY_THREE_DIGIT_EXPONENTS,
93             __acrt_rounding_mode::legacy,
94             ptd);
95 
96         if (e != 0)
97         {
98             return ptd.get_errno().set(e);
99         }
100     }
101 
102     // Remove the trailing zeroes before the exponent; we don't need to check
103     // for buffer_count:
104     char* p = buffer;
105     while (*p && *p != decimal_point)
106     {
107         ++p;
108     }
109 
110     if (*p == '\0')
111     {
112         return 0;
113     }
114 
115     ++p;
116 
117     while (*p && *p != 'e')
118     {
119         ++p;
120     }
121 
122     char* stop = p;
123     --p;
124 
125     while (*p == '0')
126     {
127         --p;
128     }
129 
130     while ((*++p = *stop++) != '\0') { }
131 
132     return 0;
133 }
134 
135 extern "C" errno_t __cdecl _gcvt_s(
136     char*  const buffer,
137     size_t const buffer_count,
138     double const value,
139     int    const precision
140     )
141 {
142     __crt_cached_ptd_host ptd;
143     return _gcvt_s_internal(buffer, buffer_count, value, precision, ptd);
144 }
145 
146 extern "C" char* __cdecl _gcvt(
147     double const value,
148     int    const precision,
149     char*  const buffer
150     )
151 {
152     errno_t const e = _gcvt_s(buffer, _CRT_UNBOUNDED_BUFFER_SIZE, value, precision);
153     if (e != 0)
154     {
155         return nullptr;
156     }
157 
158     return buffer;
159 }
160