1 | //===-- Hexadecimal Converter for printf ------------------------*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_FLOAT_HEX_CONVERTER_H |
10 | #define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_FLOAT_HEX_CONVERTER_H |
11 | |
12 | #include "src/__support/CPP/string_view.h" |
13 | #include "src/__support/FPUtil/FPBits.h" |
14 | #include "src/__support/FPUtil/rounding_mode.h" |
15 | #include "src/stdio/printf_core/converter_utils.h" |
16 | #include "src/stdio/printf_core/core_structs.h" |
17 | #include "src/stdio/printf_core/float_inf_nan_converter.h" |
18 | #include "src/stdio/printf_core/writer.h" |
19 | |
20 | #include <inttypes.h> |
21 | #include <stddef.h> |
22 | |
23 | namespace LIBC_NAMESPACE { |
24 | namespace printf_core { |
25 | |
26 | LIBC_INLINE int convert_float_hex_exp(Writer *writer, |
27 | const FormatSection &to_conv) { |
28 | using LDBits = fputil::FPBits<long double>; |
29 | using StorageType = LDBits::StorageType; |
30 | // All of the letters will be defined relative to variable a, which will be |
31 | // the appropriate case based on the name of the conversion. This converts any |
32 | // conversion name into the letter 'a' with the appropriate case. |
33 | const char a = (to_conv.conv_name & 32) | 'A'; |
34 | |
35 | bool is_negative; |
36 | int exponent; |
37 | StorageType mantissa; |
38 | bool is_inf_or_nan; |
39 | uint32_t fraction_bits; |
40 | if (to_conv.length_modifier == LengthModifier::L) { |
41 | fraction_bits = LDBits::FRACTION_LEN; |
42 | LDBits::StorageType float_raw = to_conv.conv_val_raw; |
43 | LDBits float_bits(float_raw); |
44 | is_negative = float_bits.is_neg(); |
45 | exponent = float_bits.get_explicit_exponent(); |
46 | mantissa = float_bits.get_explicit_mantissa(); |
47 | is_inf_or_nan = float_bits.is_inf_or_nan(); |
48 | } else { |
49 | using LBits = fputil::FPBits<double>; |
50 | fraction_bits = LBits::FRACTION_LEN; |
51 | LBits::StorageType float_raw = |
52 | static_cast<LBits::StorageType>(to_conv.conv_val_raw); |
53 | LBits float_bits(float_raw); |
54 | is_negative = float_bits.is_neg(); |
55 | exponent = float_bits.get_explicit_exponent(); |
56 | mantissa = float_bits.get_explicit_mantissa(); |
57 | is_inf_or_nan = float_bits.is_inf_or_nan(); |
58 | } |
59 | |
60 | if (is_inf_or_nan) |
61 | return convert_inf_nan(writer, to_conv); |
62 | |
63 | char sign_char = 0; |
64 | |
65 | if (is_negative) |
66 | sign_char = '-'; |
67 | else if ((to_conv.flags & FormatFlags::FORCE_SIGN) == FormatFlags::FORCE_SIGN) |
68 | sign_char = '+'; // FORCE_SIGN has precedence over SPACE_PREFIX |
69 | else if ((to_conv.flags & FormatFlags::SPACE_PREFIX) == |
70 | FormatFlags::SPACE_PREFIX) |
71 | sign_char = ' '; |
72 | |
73 | constexpr size_t BITS_IN_HEX_DIGIT = 4; |
74 | |
75 | // This is to handle situations where the mantissa isn't an even number of hex |
76 | // digits. This is primarily relevant for x86 80 bit long doubles, which have |
77 | // 63 bit mantissas. In the case where the mantissa is 0, however, the |
78 | // exponent should stay as 0. |
79 | if (fraction_bits % BITS_IN_HEX_DIGIT != 0 && mantissa > 0) { |
80 | exponent -= fraction_bits % BITS_IN_HEX_DIGIT; |
81 | } |
82 | |
83 | // This is the max number of digits it can take to represent the mantissa. |
84 | // Since the number is in bits, we divide by 4, and then add one to account |
85 | // for the extra implicit bit. We use the larger of the two possible values |
86 | // since the size must be constant. |
87 | constexpr size_t MANT_BUFF_LEN = |
88 | (LDBits::FRACTION_LEN / BITS_IN_HEX_DIGIT) + 1; |
89 | char mant_buffer[MANT_BUFF_LEN]; |
90 | |
91 | size_t mant_len = (fraction_bits / BITS_IN_HEX_DIGIT) + 1; |
92 | |
93 | // Precision only tracks the number of digits after the hexadecimal point, so |
94 | // we have to add one to account for the digit before the hexadecimal point. |
95 | if (to_conv.precision + 1 < static_cast<int>(mant_len) && |
96 | to_conv.precision + 1 > 0) { |
97 | const size_t intended_digits = to_conv.precision + 1; |
98 | const size_t shift_amount = |
99 | (mant_len - intended_digits) * BITS_IN_HEX_DIGIT; |
100 | |
101 | const StorageType truncated_bits = |
102 | mantissa & ((StorageType(1) << shift_amount) - 1); |
103 | const StorageType halfway_const = StorageType(1) << (shift_amount - 1); |
104 | |
105 | mantissa >>= shift_amount; |
106 | |
107 | switch (fputil::quick_get_round()) { |
108 | case FE_TONEAREST: |
109 | // Round to nearest, if it's exactly halfway then round to even. |
110 | if (truncated_bits > halfway_const) |
111 | ++mantissa; |
112 | else if (truncated_bits == halfway_const) |
113 | mantissa = mantissa + (mantissa & 1); |
114 | break; |
115 | case FE_DOWNWARD: |
116 | if (truncated_bits > 0 && is_negative) |
117 | ++mantissa; |
118 | break; |
119 | case FE_UPWARD: |
120 | if (truncated_bits > 0 && !is_negative) |
121 | ++mantissa; |
122 | break; |
123 | case FE_TOWARDZERO: |
124 | break; |
125 | } |
126 | |
127 | // If the rounding caused an overflow, shift the mantissa and adjust the |
128 | // exponent to match. |
129 | if (mantissa >= (StorageType(1) << (intended_digits * BITS_IN_HEX_DIGIT))) { |
130 | mantissa >>= BITS_IN_HEX_DIGIT; |
131 | exponent += BITS_IN_HEX_DIGIT; |
132 | } |
133 | |
134 | mant_len = intended_digits; |
135 | } |
136 | |
137 | size_t mant_cur = mant_len; |
138 | size_t first_non_zero = 1; |
139 | for (; mant_cur > 0; --mant_cur, mantissa >>= 4) { |
140 | char mant_mod_16 = static_cast<char>(mantissa) & 15; |
141 | char new_digit = static_cast<char>( |
142 | (mant_mod_16 > 9) ? (mant_mod_16 - 10 + a) : (mant_mod_16 + '0')); |
143 | mant_buffer[mant_cur - 1] = new_digit; |
144 | if (new_digit != '0' && first_non_zero < mant_cur) |
145 | first_non_zero = mant_cur; |
146 | } |
147 | |
148 | size_t mant_digits = first_non_zero; |
149 | if (to_conv.precision >= 0) |
150 | mant_digits = mant_len; |
151 | |
152 | // This approximates the number of digits it will take to represent the |
153 | // exponent. The calculation is ceil((bits * 5) / 16). Floor also works, but |
154 | // only on exact multiples of 16. We add 1 for the sign. |
155 | // Relevant sizes: |
156 | // 15 -> 5 |
157 | // 11 -> 4 |
158 | // 8 -> 3 |
159 | constexpr size_t EXP_LEN = (((LDBits::EXP_LEN * 5) + 15) / 16) + 1; |
160 | char exp_buffer[EXP_LEN]; |
161 | |
162 | bool exp_is_negative = false; |
163 | if (exponent < 0) { |
164 | exp_is_negative = true; |
165 | exponent = -exponent; |
166 | } |
167 | |
168 | size_t exp_cur = EXP_LEN; |
169 | for (; exponent > 0; --exp_cur, exponent /= 10) { |
170 | exp_buffer[exp_cur - 1] = static_cast<char>((exponent % 10) + '0'); |
171 | } |
172 | if (exp_cur == EXP_LEN) { // if nothing else was written, write a 0. |
173 | exp_buffer[EXP_LEN - 1] = '0'; |
174 | exp_cur = EXP_LEN - 1; |
175 | } |
176 | |
177 | exp_buffer[exp_cur - 1] = exp_is_negative ? '-' : '+'; |
178 | --exp_cur; |
179 | |
180 | // these are signed to prevent underflow due to negative values. The eventual |
181 | // values will always be non-negative. |
182 | size_t trailing_zeroes = 0; |
183 | int padding; |
184 | |
185 | // prefix is "0x", and always appears. |
186 | constexpr size_t PREFIX_LEN = 2; |
187 | char prefix[PREFIX_LEN]; |
188 | prefix[0] = '0'; |
189 | prefix[1] = a + ('x' - 'a'); |
190 | const cpp::string_view prefix_str(prefix, PREFIX_LEN); |
191 | |
192 | // If the precision is greater than the actual result, pad with 0s |
193 | if (to_conv.precision > static_cast<int>(mant_digits - 1)) |
194 | trailing_zeroes = to_conv.precision - (mant_digits - 1); |
195 | |
196 | bool has_hexadecimal_point = |
197 | (mant_digits > 1) || ((to_conv.flags & FormatFlags::ALTERNATE_FORM) == |
198 | FormatFlags::ALTERNATE_FORM); |
199 | constexpr cpp::string_view HEXADECIMAL_POINT("." ); |
200 | |
201 | // This is for the letter 'p' before the exponent. |
202 | const char exp_seperator = a + ('p' - 'a'); |
203 | constexpr int EXP_SEPERATOR_LEN = 1; |
204 | |
205 | padding = static_cast<int>(to_conv.min_width - (sign_char > 0 ? 1 : 0) - |
206 | PREFIX_LEN - mant_digits - trailing_zeroes - |
207 | static_cast<int>(has_hexadecimal_point) - |
208 | EXP_SEPERATOR_LEN - (EXP_LEN - exp_cur)); |
209 | if (padding < 0) |
210 | padding = 0; |
211 | |
212 | if ((to_conv.flags & FormatFlags::LEFT_JUSTIFIED) == |
213 | FormatFlags::LEFT_JUSTIFIED) { |
214 | // The pattern is (sign), 0x, digit, (.), (other digits), (zeroes), p, |
215 | // exponent, (spaces) |
216 | if (sign_char > 0) |
217 | RET_IF_RESULT_NEGATIVE(writer->write(sign_char)); |
218 | RET_IF_RESULT_NEGATIVE(writer->write(prefix_str)); |
219 | RET_IF_RESULT_NEGATIVE(writer->write(mant_buffer[0])); |
220 | if (has_hexadecimal_point) |
221 | RET_IF_RESULT_NEGATIVE(writer->write(HEXADECIMAL_POINT)); |
222 | if (mant_digits > 1) |
223 | RET_IF_RESULT_NEGATIVE(writer->write({mant_buffer + 1, mant_digits - 1})); |
224 | if (trailing_zeroes > 0) |
225 | RET_IF_RESULT_NEGATIVE(writer->write('0', trailing_zeroes)); |
226 | RET_IF_RESULT_NEGATIVE(writer->write(exp_seperator)); |
227 | RET_IF_RESULT_NEGATIVE( |
228 | writer->write({exp_buffer + exp_cur, EXP_LEN - exp_cur})); |
229 | if (padding > 0) |
230 | RET_IF_RESULT_NEGATIVE(writer->write(' ', padding)); |
231 | } else { |
232 | // The pattern is (spaces), (sign), 0x, (zeroes), digit, (.), (other |
233 | // digits), (zeroes), p, exponent |
234 | if ((padding > 0) && ((to_conv.flags & FormatFlags::LEADING_ZEROES) != |
235 | FormatFlags::LEADING_ZEROES)) |
236 | RET_IF_RESULT_NEGATIVE(writer->write(' ', padding)); |
237 | if (sign_char > 0) |
238 | RET_IF_RESULT_NEGATIVE(writer->write(sign_char)); |
239 | RET_IF_RESULT_NEGATIVE(writer->write(prefix_str)); |
240 | if ((padding > 0) && ((to_conv.flags & FormatFlags::LEADING_ZEROES) == |
241 | FormatFlags::LEADING_ZEROES)) |
242 | RET_IF_RESULT_NEGATIVE(writer->write('0', padding)); |
243 | RET_IF_RESULT_NEGATIVE(writer->write(mant_buffer[0])); |
244 | if (has_hexadecimal_point) |
245 | RET_IF_RESULT_NEGATIVE(writer->write(HEXADECIMAL_POINT)); |
246 | if (mant_digits > 1) |
247 | RET_IF_RESULT_NEGATIVE(writer->write({mant_buffer + 1, mant_digits - 1})); |
248 | if (trailing_zeroes > 0) |
249 | RET_IF_RESULT_NEGATIVE(writer->write('0', trailing_zeroes)); |
250 | RET_IF_RESULT_NEGATIVE(writer->write(exp_seperator)); |
251 | RET_IF_RESULT_NEGATIVE( |
252 | writer->write({exp_buffer + exp_cur, EXP_LEN - exp_cur})); |
253 | } |
254 | return WRITE_OK; |
255 | } |
256 | |
257 | } // namespace printf_core |
258 | } // namespace LIBC_NAMESPACE |
259 | |
260 | #endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_FLOAT_HEX_CONVERTER_H |
261 | |