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