Warning: This file is not a C or C++ file. It does not have highlighting.

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/__support/ctype_utils.h"
16#include "src/__support/macros/config.h"
17#include "src/stdio/printf_core/converter_utils.h"
18#include "src/stdio/printf_core/core_structs.h"
19#include "src/stdio/printf_core/float_inf_nan_converter.h"
20#include "src/stdio/printf_core/writer.h"
21
22#include <inttypes.h>
23#include <stddef.h>
24
25namespace LIBC_NAMESPACE_DECL {
26namespace printf_core {
27
28template <WriteMode write_mode>
29LIBC_INLINE int convert_float_hex_exp(Writer<write_mode> *writer,
30 const FormatSection &to_conv) {
31 using LDBits = fputil::FPBits<long double>;
32 using StorageType = LDBits::StorageType;
33
34 bool is_negative;
35 int exponent;
36 StorageType mantissa;
37 bool is_inf_or_nan;
38 uint32_t fraction_bits;
39 if (to_conv.length_modifier == LengthModifier::L) {
40 fraction_bits = LDBits::FRACTION_LEN;
41 LDBits::StorageType float_raw = to_conv.conv_val_raw;
42 LDBits float_bits(float_raw);
43 is_negative = float_bits.is_neg();
44 exponent = float_bits.get_explicit_exponent();
45 mantissa = float_bits.get_explicit_mantissa();
46 is_inf_or_nan = float_bits.is_inf_or_nan();
47 } else {
48 using LBits = fputil::FPBits<double>;
49 fraction_bits = LBits::FRACTION_LEN;
50 LBits::StorageType float_raw =
51 static_cast<LBits::StorageType>(to_conv.conv_val_raw);
52 LBits float_bits(float_raw);
53 is_negative = float_bits.is_neg();
54 exponent = float_bits.get_explicit_exponent();
55 mantissa = float_bits.get_explicit_mantissa();
56 is_inf_or_nan = float_bits.is_inf_or_nan();
57 }
58
59 if (is_inf_or_nan)
60 return convert_inf_nan(writer, to_conv);
61
62 char sign_char = 0;
63
64 if (is_negative)
65 sign_char = '-';
66 else if ((to_conv.flags & FormatFlags::FORCE_SIGN) == FormatFlags::FORCE_SIGN)
67 sign_char = '+'; // FORCE_SIGN has precedence over SPACE_PREFIX
68 else if ((to_conv.flags & FormatFlags::SPACE_PREFIX) ==
69 FormatFlags::SPACE_PREFIX)
70 sign_char = ' ';
71
72 constexpr size_t BITS_IN_HEX_DIGIT = 4;
73
74 // This is to handle situations where the mantissa isn't an even number of hex
75 // digits. This is primarily relevant for x86 80 bit long doubles, which have
76 // 63 bit mantissas. In the case where the mantissa is 0, however, the
77 // exponent should stay as 0.
78 if (fraction_bits % BITS_IN_HEX_DIGIT != 0 && mantissa > 0) {
79 exponent -= fraction_bits % BITS_IN_HEX_DIGIT;
80 }
81
82 // This is the max number of digits it can take to represent the mantissa.
83 // Since the number is in bits, we divide by 4, and then add one to account
84 // for the extra implicit bit. We use the larger of the two possible values
85 // since the size must be constant.
86 constexpr size_t MANT_BUFF_LEN =
87 (LDBits::FRACTION_LEN / BITS_IN_HEX_DIGIT) + 1;
88 char mant_buffer[MANT_BUFF_LEN];
89
90 size_t mant_len = (fraction_bits / BITS_IN_HEX_DIGIT) + 1;
91
92 // Precision only tracks the number of digits after the hexadecimal point, so
93 // we have to add one to account for the digit before the hexadecimal point.
94 if (to_conv.precision + 1 < static_cast<int>(mant_len) &&
95 to_conv.precision + 1 > 0) {
96 const size_t intended_digits = to_conv.precision + 1;
97 const size_t shift_amount =
98 (mant_len - intended_digits) * BITS_IN_HEX_DIGIT;
99
100 const StorageType truncated_bits =
101 mantissa & ((StorageType(1) << shift_amount) - 1);
102 const StorageType halfway_const = StorageType(1) << (shift_amount - 1);
103
104 mantissa >>= shift_amount;
105
106 switch (fputil::quick_get_round()) {
107 case FE_TONEAREST:
108 // Round to nearest, if it's exactly halfway then round to even.
109 if (truncated_bits > halfway_const)
110 ++mantissa;
111 else if (truncated_bits == halfway_const)
112 mantissa = mantissa + (mantissa & 1);
113 break;
114 case FE_DOWNWARD:
115 if (truncated_bits > 0 && is_negative)
116 ++mantissa;
117 break;
118 case FE_UPWARD:
119 if (truncated_bits > 0 && !is_negative)
120 ++mantissa;
121 break;
122 case FE_TOWARDZERO:
123 break;
124 }
125
126 // If the rounding caused an overflow, shift the mantissa and adjust the
127 // exponent to match.
128 if (mantissa >= (StorageType(1) << (intended_digits * BITS_IN_HEX_DIGIT))) {
129 mantissa >>= BITS_IN_HEX_DIGIT;
130 exponent += BITS_IN_HEX_DIGIT;
131 }
132
133 mant_len = intended_digits;
134 }
135
136 size_t mant_cur = mant_len;
137 size_t first_non_zero = 1;
138 for (; mant_cur > 0; --mant_cur, mantissa >>= 4) {
139 char mant_mod_16 = static_cast<char>(mantissa % 16);
140 char new_digit = static_cast<char>(internal::int_to_b36_char(mant_mod_16));
141 if (internal::isupper(to_conv.conv_name))
142 new_digit = static_cast<char>(internal::toupper(new_digit));
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] =
171 static_cast<char>(internal::int_to_b36_char(exponent % 10));
172 }
173 if (exp_cur == EXP_LEN) { // if nothing else was written, write a 0.
174 exp_buffer[EXP_LEN - 1] = '0';
175 exp_cur = EXP_LEN - 1;
176 }
177
178 exp_buffer[exp_cur - 1] = exp_is_negative ? '-' : '+';
179 --exp_cur;
180
181 // these are signed to prevent underflow due to negative values. The eventual
182 // values will always be non-negative.
183 size_t trailing_zeroes = 0;
184 int padding;
185
186 // prefix is "0x", and always appears.
187 constexpr size_t PREFIX_LEN = 2;
188 char prefix[PREFIX_LEN];
189 prefix[0] = '0';
190 prefix[1] = internal::islower(to_conv.conv_name) ? 'x' : 'X';
191 const cpp::string_view prefix_str(prefix, PREFIX_LEN);
192
193 // If the precision is greater than the actual result, pad with 0s
194 if (to_conv.precision > static_cast<int>(mant_digits - 1))
195 trailing_zeroes = to_conv.precision - (mant_digits - 1);
196
197 bool has_hexadecimal_point =
198 (mant_digits > 1) || ((to_conv.flags & FormatFlags::ALTERNATE_FORM) ==
199 FormatFlags::ALTERNATE_FORM);
200 constexpr cpp::string_view HEXADECIMAL_POINT(".");
201
202 // This is for the letter 'p' before the exponent.
203 const char exp_separator = internal::islower(to_conv.conv_name) ? 'p' : 'P';
204 constexpr int EXP_SEPARATOR_LEN = 1;
205
206 padding = static_cast<int>(to_conv.min_width - (sign_char > 0 ? 1 : 0) -
207 PREFIX_LEN - mant_digits - trailing_zeroes -
208 static_cast<int>(has_hexadecimal_point) -
209 EXP_SEPARATOR_LEN - (EXP_LEN - exp_cur));
210 if (padding < 0)
211 padding = 0;
212
213 if ((to_conv.flags & FormatFlags::LEFT_JUSTIFIED) ==
214 FormatFlags::LEFT_JUSTIFIED) {
215 // The pattern is (sign), 0x, digit, (.), (other digits), (zeroes), p,
216 // exponent, (spaces)
217 if (sign_char > 0)
218 RET_IF_RESULT_NEGATIVE(writer->write(sign_char));
219 RET_IF_RESULT_NEGATIVE(writer->write(prefix_str));
220 RET_IF_RESULT_NEGATIVE(writer->write(mant_buffer[0]));
221 if (has_hexadecimal_point)
222 RET_IF_RESULT_NEGATIVE(writer->write(HEXADECIMAL_POINT));
223 if (mant_digits > 1)
224 RET_IF_RESULT_NEGATIVE(writer->write({mant_buffer + 1, mant_digits - 1}));
225 if (trailing_zeroes > 0)
226 RET_IF_RESULT_NEGATIVE(writer->write('0', trailing_zeroes));
227 RET_IF_RESULT_NEGATIVE(writer->write(exp_separator));
228 RET_IF_RESULT_NEGATIVE(
229 writer->write({exp_buffer + exp_cur, EXP_LEN - exp_cur}));
230 if (padding > 0)
231 RET_IF_RESULT_NEGATIVE(writer->write(' ', padding));
232 } else {
233 // The pattern is (spaces), (sign), 0x, (zeroes), digit, (.), (other
234 // digits), (zeroes), p, exponent
235 if ((padding > 0) && ((to_conv.flags & FormatFlags::LEADING_ZEROES) !=
236 FormatFlags::LEADING_ZEROES))
237 RET_IF_RESULT_NEGATIVE(writer->write(' ', padding));
238 if (sign_char > 0)
239 RET_IF_RESULT_NEGATIVE(writer->write(sign_char));
240 RET_IF_RESULT_NEGATIVE(writer->write(prefix_str));
241 if ((padding > 0) && ((to_conv.flags & FormatFlags::LEADING_ZEROES) ==
242 FormatFlags::LEADING_ZEROES))
243 RET_IF_RESULT_NEGATIVE(writer->write('0', padding));
244 RET_IF_RESULT_NEGATIVE(writer->write(mant_buffer[0]));
245 if (has_hexadecimal_point)
246 RET_IF_RESULT_NEGATIVE(writer->write(HEXADECIMAL_POINT));
247 if (mant_digits > 1)
248 RET_IF_RESULT_NEGATIVE(writer->write({mant_buffer + 1, mant_digits - 1}));
249 if (trailing_zeroes > 0)
250 RET_IF_RESULT_NEGATIVE(writer->write('0', trailing_zeroes));
251 RET_IF_RESULT_NEGATIVE(writer->write(exp_separator));
252 RET_IF_RESULT_NEGATIVE(
253 writer->write({exp_buffer + exp_cur, EXP_LEN - exp_cur}));
254 }
255 return WRITE_OK;
256}
257
258} // namespace printf_core
259} // namespace LIBC_NAMESPACE_DECL
260
261#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_FLOAT_HEX_CONVERTER_H
262

Warning: This file is not a C or C++ file. It does not have highlighting.

source code of libc/src/stdio/printf_core/float_hex_converter.h