Reactos
at listview 173 lines 5.6 kB view raw
1// 2// _fptostr.cpp 3// 4// Copyright (c) Microsoft Corporation. All rights reserved. 5// 6// Defines the internal function that writes a mantissa (in a STRFLT) into a 7// buffer. This function puts all of the digits into the buffer, handles 8// rounding based on the requested precision (digits), and updates the decimal 9// point position in the STRFLT object. Note that this function does not change 10// the mantissa field of the STRFLT, so callers of this function may rely on it 11// being unmodified. 12// 13#include <corecrt_internal.h> 14#include <corecrt_internal_fltintrn.h> 15#include <corecrt_internal_ptd_propagation.h> 16#include <fenv.h> 17#include <string.h> 18#include <stddef.h> 19 20// __acrt_has_trailing_digits::no_trailing isn't indicative of how to round if we're rounding to a point before the end of the generated digits. 21// __acrt_has_trailing_digits::no_trailing only says if there are any digits after the mantisa we got. 22// We need to ensure the remaining generated digits are all zero before choosing rounds-to-even behavior intended for exactly representable floating point numbers. 23static bool check_trailing(char const * mantissa_it, __acrt_has_trailing_digits const trailing_digits) 24{ 25 if (trailing_digits == __acrt_has_trailing_digits::trailing) 26 { 27 return true; 28 } 29 30 while (*mantissa_it == '0') 31 { 32 mantissa_it++; 33 } 34 35 if (*mantissa_it != '\0') 36 { 37 return true; 38 } 39 40 return false; 41} 42 43static bool should_round_up( 44 char const * const mantissa_base, 45 char const * const mantissa_it, 46 int const sign, 47 __acrt_has_trailing_digits const trailing_digits, 48 __acrt_rounding_mode const rounding_mode 49) 50{ 51 if (rounding_mode == __acrt_rounding_mode::legacy) 52 { 53 return *mantissa_it >= '5'; 54 } 55 56 int const round_mode = fegetround(); 57 58 if (round_mode == FE_TONEAREST) 59 { 60 if (*mantissa_it > '5') 61 { 62 return true; 63 } 64 65 if (*mantissa_it < '5') 66 { 67 return false; 68 } 69 70 // If there are trailing digits we are in a scenario like this .5000000001 and should round up 71 if (check_trailing(mantissa_it + 1, trailing_digits)) 72 { 73 return true; 74 } 75 76 // At this point, the number is exactly representable and we are rounding 5. 77 // In this case, IEEE 754 states to round towards the nearest even number. 78 // Therefore: if the previous digit is odd, we round up (1.5 -> 2). 79 // if the previous digit is even, we round down (2.5 -> 2). 80 81 // If there is no preceding digit, it is considered zero, so round down. 82 if (mantissa_it == mantissa_base) 83 { 84 return false; 85 } 86 87 // If the previous digit is odd, we should round up to the closest even. 88 return *(mantissa_it - 1) % 2; 89 } 90 91 if (round_mode == FE_UPWARD) 92 { 93 return check_trailing(mantissa_it, trailing_digits) && sign != '-'; 94 } 95 96 if (round_mode == FE_DOWNWARD) 97 { 98 return check_trailing(mantissa_it, trailing_digits) && sign == '-'; 99 } 100 101 return false; 102} 103 104extern "C" errno_t __cdecl __acrt_fp_strflt_to_string( 105 char* const buffer, 106 size_t const buffer_count, 107 int digits, 108 STRFLT const pflt, 109 __acrt_has_trailing_digits const trailing_digits, 110 __acrt_rounding_mode const rounding_mode, 111 __crt_cached_ptd_host& ptd 112) 113{ 114 _UCRT_VALIDATE_RETURN_ERRCODE(ptd, buffer != nullptr, EINVAL); 115 _UCRT_VALIDATE_RETURN_ERRCODE(ptd, buffer_count > 0, EINVAL); 116 buffer[0] = '\0'; 117 118 _UCRT_VALIDATE_RETURN_ERRCODE(ptd, buffer_count > static_cast<size_t>((digits > 0 ? digits : 0) + 1), ERANGE); 119 _UCRT_VALIDATE_RETURN_ERRCODE(ptd, pflt != nullptr, EINVAL); 120 121 char* buffer_it = buffer; 122 char* const mantissa_base = pflt->mantissa; 123 char* mantissa_it = pflt->mantissa; 124 125 // The buffer will contain 'digits' decimal digits plus an optional overflow 126 // digit for the rounding. 127 128 // Initialize the first digit in the buffer to '0' (Note: not '\0') and set 129 // the pointer to the second digit of the buffer. The first digit is used 130 // to handle overflow on rounding (e.g. 9.999... becomes 10.000...), which 131 // requires a carry into the first digit. 132 *buffer_it++ = '0'; 133 134 // Copy the digits of the value into the buffer (with '0' padding) and 135 // insert the null terminator: 136 while (digits > 0) 137 { 138 *buffer_it++ = *mantissa_it ? *mantissa_it++ : '0'; 139 --digits; 140 } 141 142 *buffer_it = '\0'; 143 144 // Do any rounding which may be needed. Note: if digits < 0, we don't do 145 // any rounding because in this case, the rounding occurs in a digit which 146 // will not be output because of the precision requested. 147 if (digits >= 0 && should_round_up(mantissa_base, mantissa_it, pflt->sign, trailing_digits, rounding_mode)) 148 { 149 buffer_it--; 150 151 while (*buffer_it == '9') 152 { 153 *buffer_it-- = '0'; 154 } 155 156 *buffer_it += 1; 157 } 158 159 if (*buffer == '1') 160 { 161 // The rounding caused overflow into the leading digit (e.g. 9.999... 162 // became 10.000...), so increment the decimal point position by 1: 163 pflt->decpt++; 164 } 165 else 166 { 167 // Move the entire string to the left one digit to remove the unused 168 // overflow digit: 169 memmove(buffer, buffer + 1, strlen(buffer + 1) + 1); 170 } 171 172 return 0; 173}