1 // 2 // fcvt.cpp 3 // 4 // Copyright (c) Microsoft Corporation. All rights reserved. 5 // 6 // Defines the _ecvt and _fcvt families of functions. 7 // 8 #include <corecrt_internal.h> 9 #include <corecrt_internal_fltintrn.h> 10 #include <corecrt_internal_ptd_propagation.h> 11 #include <corecrt_internal_securecrt.h> 12 #include <minmax.h> 13 #include <stdlib.h> 14 15 16 17 // Tries to get the pre-thread conversion buffer; returns nullptr on failure. 18 static char* __cdecl try_get_ptd_buffer(__crt_cached_ptd_host& ptd) 19 { 20 __acrt_ptd* const raw_ptd = ptd.get_raw_ptd_noexit(); 21 if (!raw_ptd) 22 { 23 return nullptr; 24 } 25 26 if (raw_ptd->_cvtbuf) 27 { 28 return raw_ptd->_cvtbuf; 29 } 30 31 raw_ptd->_cvtbuf = _malloc_crt_t(char, _CVTBUFSIZE).detach(); 32 33 return raw_ptd->_cvtbuf; 34 } 35 36 37 38 // An internal helper that wraps the call to convert the STRFLT to a string and 39 // updates all of the data that is used by its callers. 40 static errno_t __cdecl internal_to_string( 41 _Out_writes_z_(buffer_count) char* const buffer, 42 size_t const buffer_count, 43 STRFLT const strflt, 44 int const requested_digits, 45 int* const decimal_point, 46 int* const sign, 47 __crt_cached_ptd_host& ptd 48 ) throw() 49 { 50 // Make sure we don't overflow the buffer. If the user asks for more digits 51 // than the buffer can handle, truncate it to the maximum size allowed in 52 // the buffer. The maximum size is two less than the buffer size because we 53 // use one character for overflow and one for the null terminator. 54 size_t const minimum_buffer_count = static_cast<size_t>((requested_digits > 0 ? requested_digits : 0) + 2); 55 56 _UCRT_VALIDATE_RETURN_ERRCODE(ptd, buffer_count >= minimum_buffer_count, ERANGE); 57 58 int const capped_digits = min(requested_digits, static_cast<int>(buffer_count - 2)); 59 60 errno_t const e = __acrt_fp_strflt_to_string(buffer, buffer_count, capped_digits, strflt, __acrt_has_trailing_digits::trailing, __acrt_rounding_mode::legacy, ptd); 61 62 if (e != 0) 63 { 64 return ptd.get_errno().set(e); 65 } 66 67 *sign = strflt->sign == '-' ? 1 : 0; 68 *decimal_point = strflt->decpt; 69 70 return 0; 71 } 72 73 74 75 // The _fcvt functions, like the _ecvt functions, convert a floating point value 76 // to a narrow character string. The functions prepare the data for the Fortran 77 // F-format with the number of digits following the decimal point specified by 78 // requested_digits. The position of the decimal point is returned indirectly 79 // through *decimal_point. The correct digit for Fortran F-format is rounded. 80 // 81 // These functions update either (a) the user-provided string (_s-suffixed 82 // function) or (b) the per-thread conversion buffer. The _s-suffixed 83 // function returns zero on success, or an error code on failure. The 84 // *decimal_point and *sign values are updated with the results of the 85 // conversion. 86 static errno_t __cdecl _fcvt_s_internal( 87 char* const buffer, 88 size_t const buffer_count, 89 double const value, 90 int const requested_digits, 91 int* const decimal_point, 92 int* const sign, 93 __crt_cached_ptd_host& ptd 94 ) 95 { 96 _UCRT_VALIDATE_RETURN_ERRCODE(ptd, buffer != nullptr, EINVAL); 97 _UCRT_VALIDATE_RETURN_ERRCODE(ptd, buffer_count > 0, EINVAL); 98 _RESET_STRING(buffer, buffer_count); 99 _UCRT_VALIDATE_RETURN_ERRCODE(ptd, decimal_point != nullptr, EINVAL); 100 _UCRT_VALIDATE_RETURN_ERRCODE(ptd, sign != nullptr, EINVAL); 101 102 char result_string[_CVTBUFSIZE + 1]; 103 104 _strflt strflt{}; 105 __acrt_fltout( 106 reinterpret_cast<_CRT_DOUBLE const&>(value), 107 _countof(result_string), 108 __acrt_precision_style::fixed, 109 &strflt, 110 result_string, 111 _countof(result_string)); 112 113 int const actual_digits = strflt.decpt + requested_digits; 114 115 bool const buffer_insufficiently_large = 116 requested_digits > 0 && strflt.decpt > 0 && 117 actual_digits < requested_digits; 118 119 int const capped_digits = buffer_insufficiently_large ? INT_MAX : actual_digits; 120 121 return internal_to_string(buffer, buffer_count, &strflt, capped_digits, decimal_point, sign, ptd); 122 } 123 124 extern "C" errno_t __cdecl _fcvt_s( 125 char* const buffer, 126 size_t const buffer_count, 127 double const value, 128 int const requested_digits, 129 int* const decimal_point, 130 int* const sign 131 ) 132 { 133 __crt_cached_ptd_host ptd; 134 return _fcvt_s_internal(buffer, buffer_count, value, requested_digits, decimal_point, sign, ptd); 135 } 136 137 static char* __cdecl _fcvt_internal( 138 double const value, 139 int const requested_digits, 140 int* const decimal_point, 141 int* const sign, 142 __crt_cached_ptd_host& ptd 143 ) 144 { 145 char* const buffer = try_get_ptd_buffer(ptd); 146 if (!buffer) 147 { 148 return nullptr; 149 } 150 151 char result_string[_CVTBUFSIZE + 1]; 152 153 _strflt strflt{}; 154 __acrt_fltout( 155 reinterpret_cast<_CRT_DOUBLE const&>(value), 156 _countof(result_string), 157 __acrt_precision_style::fixed, 158 &strflt, 159 result_string, 160 _countof(result_string)); 161 162 // Make sure we don't overflow the buffer. If the user asks for more digits 163 // than the buffer can handle, truncate it to the maximum size allowed in 164 // the buffer. The maximum size is two less than the buffer size because we 165 // use one character for overflow and one for the null terminator. 166 int const capped_digits = min(requested_digits, _CVTBUFSIZE - 2 - strflt.decpt); 167 168 errno_t const status = _fcvt_s_internal(buffer, _CVTBUFSIZE, value, capped_digits, decimal_point, sign, ptd); 169 if (status != 0) 170 { 171 return nullptr; 172 } 173 174 return buffer; 175 } 176 177 extern "C" char* __cdecl _fcvt( 178 double const value, 179 int const requested_digits, 180 int* const decimal_point, 181 int* const sign 182 ) 183 { 184 __crt_cached_ptd_host ptd; 185 return _fcvt_internal(value, requested_digits, decimal_point, sign, ptd); 186 } 187 188 189 // The _ecvt functions, which convert a floating point value to a string. The 190 // position of the decimal point relative to the beginning of the string is 191 // stored indirectly through the decimal_point argument, where a negative value 192 // means that the decimal point is to the left of the returned digits. If the 193 // sign of the result is negative, the word pointed to by sign is nonzero; 194 // otherwise it is zero. The low order digit is rounded. 195 // 196 // These functions update either (a) the user-provided string (_s-suffixed 197 // function) or (b) the per-thread conversion buffer. The _s-suffixed 198 // function returns zero on success, or an error code on failure. The 199 // *decimal_point and *sign values are updated with the results of the 200 // conversion. 201 static errno_t __cdecl _ecvt_s_internal( 202 char* const buffer, 203 size_t const buffer_count, 204 double const value, 205 int const requested_digits, 206 int* const decimal_point, 207 int* const sign, 208 __crt_cached_ptd_host& ptd 209 ) 210 { 211 _UCRT_VALIDATE_RETURN_ERRCODE(ptd, buffer != nullptr, EINVAL); 212 _UCRT_VALIDATE_RETURN_ERRCODE(ptd, buffer_count > 0, EINVAL); 213 _RESET_STRING(buffer, buffer_count); 214 _UCRT_VALIDATE_RETURN_ERRCODE(ptd, decimal_point != nullptr, EINVAL); 215 _UCRT_VALIDATE_RETURN_ERRCODE(ptd, sign != nullptr, EINVAL); 216 217 char result_string[_CVTBUFSIZE + 1]; 218 219 _strflt strflt{}; 220 __acrt_fltout( 221 reinterpret_cast<_CRT_DOUBLE const&>(value), 222 _countof(result_string), 223 __acrt_precision_style::fixed, 224 &strflt, 225 result_string, 226 _countof(result_string)); 227 228 errno_t const e = internal_to_string(buffer, buffer_count, &strflt, requested_digits, decimal_point, sign, ptd); 229 230 // Make sure we don't overflow the buffer. If the user asks for more digits 231 // than the buffer can handle, truncate it to the maximum size allowed in 232 // the buffer. The maximum size is two less than the buffer size because we 233 // use one character for overflow and one for the null terminator. 234 int const capped_digits = min(requested_digits, static_cast<int>(buffer_count - 2)); 235 236 // The conversion function occasionally returns an extra char in the buffer: 237 if (capped_digits >= 0 && buffer[capped_digits]) 238 { 239 buffer[capped_digits] = '\0'; 240 } 241 242 return e; 243 } 244 245 extern "C" errno_t __cdecl _ecvt_s( 246 char* const buffer, 247 size_t const buffer_count, 248 double const value, 249 int const requested_digits, 250 int* const decimal_point, 251 int* const sign 252 ) 253 { 254 __crt_cached_ptd_host ptd; 255 return _ecvt_s_internal(buffer, buffer_count, value, requested_digits, decimal_point, sign, ptd); 256 } 257 258 static char* __cdecl _ecvt_internal( 259 double const value, 260 int const requested_digits, 261 int* const decimal_point, 262 int* const sign, 263 __crt_cached_ptd_host& ptd 264 ) 265 { 266 char* const buffer = try_get_ptd_buffer(ptd); 267 if (!buffer) 268 { 269 return nullptr; 270 } 271 272 // Make sure we don't overflow the buffer. If the user asks for more digits 273 // than the buffer can handle, truncate it to the maximum size allowed in 274 // the buffer. The maximum size is two less than the buffer size because we 275 // use one character for overflow and one for the null terminator. 276 int const capped_digits = min(requested_digits, _CVTBUFSIZE - 2); 277 278 errno_t const e = _ecvt_s_internal(buffer, _CVTBUFSIZE, value, capped_digits, decimal_point, sign, ptd); 279 if (e != 0) 280 { 281 return nullptr; 282 } 283 284 return buffer; 285 } 286 287 extern "C" char* __cdecl _ecvt( 288 double const value, 289 int const requested_digits, 290 int* const decimal_point, 291 int* const sign 292 ) 293 { 294 __crt_cached_ptd_host ptd; 295 return _ecvt_internal(value, requested_digits, decimal_point, sign, ptd); 296 } 297