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