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.
check_trailing(char const * mantissa_it,__acrt_has_trailing_digits const trailing_digits)23 static 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
should_round_up(char const * const mantissa_base,char const * const mantissa_it,int const sign,__acrt_has_trailing_digits const trailing_digits,__acrt_rounding_mode const rounding_mode)43 static 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
__acrt_fp_strflt_to_string(char * const buffer,size_t const buffer_count,int digits,STRFLT const pflt,__acrt_has_trailing_digits const trailing_digits,__acrt_rounding_mode const rounding_mode,__crt_cached_ptd_host & ptd)104 extern "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 }
174