xref: /llvm-project/libc/src/stdio/printf_core/float_hex_converter.h (revision a0c4f854cad2b97e44a1b58dc1fd982e1c4d60f3)
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 
25 namespace LIBC_NAMESPACE_DECL {
26 namespace printf_core {
27 
28 LIBC_INLINE int convert_float_hex_exp(Writer *writer,
29                                       const FormatSection &to_conv) {
30   using LDBits = fputil::FPBits<long double>;
31   using StorageType = LDBits::StorageType;
32 
33   bool is_negative;
34   int exponent;
35   StorageType mantissa;
36   bool is_inf_or_nan;
37   uint32_t fraction_bits;
38   if (to_conv.length_modifier == LengthModifier::L) {
39     fraction_bits = LDBits::FRACTION_LEN;
40     LDBits::StorageType float_raw = to_conv.conv_val_raw;
41     LDBits float_bits(float_raw);
42     is_negative = float_bits.is_neg();
43     exponent = float_bits.get_explicit_exponent();
44     mantissa = float_bits.get_explicit_mantissa();
45     is_inf_or_nan = float_bits.is_inf_or_nan();
46   } else {
47     using LBits = fputil::FPBits<double>;
48     fraction_bits = LBits::FRACTION_LEN;
49     LBits::StorageType float_raw =
50         static_cast<LBits::StorageType>(to_conv.conv_val_raw);
51     LBits float_bits(float_raw);
52     is_negative = float_bits.is_neg();
53     exponent = float_bits.get_explicit_exponent();
54     mantissa = float_bits.get_explicit_mantissa();
55     is_inf_or_nan = float_bits.is_inf_or_nan();
56   }
57 
58   if (is_inf_or_nan)
59     return convert_inf_nan(writer, to_conv);
60 
61   char sign_char = 0;
62 
63   if (is_negative)
64     sign_char = '-';
65   else if ((to_conv.flags & FormatFlags::FORCE_SIGN) == FormatFlags::FORCE_SIGN)
66     sign_char = '+'; // FORCE_SIGN has precedence over SPACE_PREFIX
67   else if ((to_conv.flags & FormatFlags::SPACE_PREFIX) ==
68            FormatFlags::SPACE_PREFIX)
69     sign_char = ' ';
70 
71   constexpr size_t BITS_IN_HEX_DIGIT = 4;
72 
73   // This is to handle situations where the mantissa isn't an even number of hex
74   // digits. This is primarily relevant for x86 80 bit long doubles, which have
75   // 63 bit mantissas. In the case where the mantissa is 0, however, the
76   // exponent should stay as 0.
77   if (fraction_bits % BITS_IN_HEX_DIGIT != 0 && mantissa > 0) {
78     exponent -= fraction_bits % BITS_IN_HEX_DIGIT;
79   }
80 
81   // This is the max number of digits it can take to represent the mantissa.
82   // Since the number is in bits, we divide by 4, and then add one to account
83   // for the extra implicit bit. We use the larger of the two possible values
84   // since the size must be constant.
85   constexpr size_t MANT_BUFF_LEN =
86       (LDBits::FRACTION_LEN / BITS_IN_HEX_DIGIT) + 1;
87   char mant_buffer[MANT_BUFF_LEN];
88 
89   size_t mant_len = (fraction_bits / BITS_IN_HEX_DIGIT) + 1;
90 
91   // Precision only tracks the number of digits after the hexadecimal point, so
92   // we have to add one to account for the digit before the hexadecimal point.
93   if (to_conv.precision + 1 < static_cast<int>(mant_len) &&
94       to_conv.precision + 1 > 0) {
95     const size_t intended_digits = to_conv.precision + 1;
96     const size_t shift_amount =
97         (mant_len - intended_digits) * BITS_IN_HEX_DIGIT;
98 
99     const StorageType truncated_bits =
100         mantissa & ((StorageType(1) << shift_amount) - 1);
101     const StorageType halfway_const = StorageType(1) << (shift_amount - 1);
102 
103     mantissa >>= shift_amount;
104 
105     switch (fputil::quick_get_round()) {
106     case FE_TONEAREST:
107       // Round to nearest, if it's exactly halfway then round to even.
108       if (truncated_bits > halfway_const)
109         ++mantissa;
110       else if (truncated_bits == halfway_const)
111         mantissa = mantissa + (mantissa & 1);
112       break;
113     case FE_DOWNWARD:
114       if (truncated_bits > 0 && is_negative)
115         ++mantissa;
116       break;
117     case FE_UPWARD:
118       if (truncated_bits > 0 && !is_negative)
119         ++mantissa;
120       break;
121     case FE_TOWARDZERO:
122       break;
123     }
124 
125     // If the rounding caused an overflow, shift the mantissa and adjust the
126     // exponent to match.
127     if (mantissa >= (StorageType(1) << (intended_digits * BITS_IN_HEX_DIGIT))) {
128       mantissa >>= BITS_IN_HEX_DIGIT;
129       exponent += BITS_IN_HEX_DIGIT;
130     }
131 
132     mant_len = intended_digits;
133   }
134 
135   size_t mant_cur = mant_len;
136   size_t first_non_zero = 1;
137   for (; mant_cur > 0; --mant_cur, mantissa >>= 4) {
138     char mant_mod_16 = static_cast<char>(mantissa % 16);
139     char new_digit = static_cast<char>(internal::int_to_b36_char(mant_mod_16));
140     if (internal::isupper(to_conv.conv_name))
141       new_digit = static_cast<char>(internal::toupper(new_digit));
142     mant_buffer[mant_cur - 1] = new_digit;
143     if (new_digit != '0' && first_non_zero < mant_cur)
144       first_non_zero = mant_cur;
145   }
146 
147   size_t mant_digits = first_non_zero;
148   if (to_conv.precision >= 0)
149     mant_digits = mant_len;
150 
151   // This approximates the number of digits it will take to represent the
152   // exponent. The calculation is ceil((bits * 5) / 16). Floor also works, but
153   // only on exact multiples of 16. We add 1 for the sign.
154   // Relevant sizes:
155   // 15 -> 5
156   // 11 -> 4
157   // 8  -> 3
158   constexpr size_t EXP_LEN = (((LDBits::EXP_LEN * 5) + 15) / 16) + 1;
159   char exp_buffer[EXP_LEN];
160 
161   bool exp_is_negative = false;
162   if (exponent < 0) {
163     exp_is_negative = true;
164     exponent = -exponent;
165   }
166 
167   size_t exp_cur = EXP_LEN;
168   for (; exponent > 0; --exp_cur, exponent /= 10) {
169     exp_buffer[exp_cur - 1] =
170         static_cast<char>(internal::int_to_b36_char(exponent % 10));
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] = internal::islower(to_conv.conv_name) ? 'x' : 'X';
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_separator = internal::islower(to_conv.conv_name) ? 'p' : 'P';
203   constexpr int EXP_SEPARATOR_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_SEPARATOR_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_separator));
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_separator));
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_DECL
259 
260 #endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_FLOAT_HEX_CONVERTER_H
261