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