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