| //===-- Decimal Float Converter for printf ----------------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_FLOAT_DEC_CONVERTER_H |
| #define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_FLOAT_DEC_CONVERTER_H |
| |
| #include "src/__support/CPP/string_view.h" |
| #include "src/__support/FPUtil/FEnvImpl.h" |
| #include "src/__support/FPUtil/FPBits.h" |
| #include "src/__support/FPUtil/FloatProperties.h" |
| #include "src/__support/UInt.h" |
| #include "src/__support/UInt128.h" |
| #include "src/__support/common.h" |
| #include "src/__support/float_to_string.h" |
| #include "src/__support/integer_to_string.h" |
| #include "src/stdio/printf_core/converter_utils.h" |
| #include "src/stdio/printf_core/core_structs.h" |
| #include "src/stdio/printf_core/float_inf_nan_converter.h" |
| #include "src/stdio/printf_core/writer.h" |
| |
| #include <inttypes.h> |
| #include <stddef.h> |
| |
| namespace __llvm_libc { |
| namespace printf_core { |
| |
| using MantissaInt = fputil::FPBits<long double>::UIntType; |
| |
| // Returns true if value is divisible by 2^p. |
| LIBC_INLINE constexpr bool multiple_of_power_of_2(const uint64_t value, |
| const uint32_t p) { |
| return (value & ((uint64_t(1) << p) - 1)) == 0; |
| } |
| |
| constexpr size_t BLOCK_SIZE = 9; |
| constexpr uint32_t MAX_BLOCK = 999999999; |
| |
| // constexpr size_t BLOCK_SIZE = 18; |
| // constexpr uint32_t MAX_BLOCK = 999999999999999999; |
| constexpr char DECIMAL_POINT = '.'; |
| |
| // This is used to represent which direction the number should be rounded. |
| enum class RoundDirection { Up, Down, Even }; |
| |
| class PaddingWriter { |
| bool left_justified = false; |
| bool leading_zeroes = false; |
| char sign_char = 0; |
| size_t min_width = 0; |
| |
| public: |
| PaddingWriter() {} |
| PaddingWriter(const FormatSection &to_conv, char init_sign_char) |
| : left_justified((to_conv.flags & FormatFlags::LEFT_JUSTIFIED) > 0), |
| leading_zeroes((to_conv.flags & FormatFlags::LEADING_ZEROES) > 0), |
| sign_char(init_sign_char), |
| min_width(to_conv.min_width > 0 ? to_conv.min_width : 0) {} |
| |
| int write_left_padding(Writer *writer, size_t total_digits) { |
| // The pattern is (spaces) (sign) (zeroes), but only one of spaces and |
| // zeroes can be written, and only if the padding amount is positive. |
| int padding_amount = min_width - total_digits - (sign_char > 0 ? 1 : 0); |
| if (left_justified || padding_amount < 0) { |
| if (sign_char > 0) { |
| RET_IF_RESULT_NEGATIVE(writer->write(sign_char)); |
| } |
| return 0; |
| } |
| if (!leading_zeroes) { |
| RET_IF_RESULT_NEGATIVE(writer->write(' ', padding_amount)); |
| } |
| if (sign_char > 0) { |
| RET_IF_RESULT_NEGATIVE(writer->write(sign_char)); |
| } |
| if (leading_zeroes) { |
| RET_IF_RESULT_NEGATIVE(writer->write('0', padding_amount)); |
| } |
| return 0; |
| } |
| |
| int write_right_padding(Writer *writer, size_t total_digits) { |
| // If and only if the conversion is left justified, there may be trailing |
| // spaces. |
| int padding_amount = min_width - total_digits - (sign_char > 0 ? 1 : 0); |
| if (left_justified && padding_amount > 0) { |
| RET_IF_RESULT_NEGATIVE(writer->write(' ', padding_amount)); |
| } |
| return 0; |
| } |
| }; |
| |
| /* |
| We only need to round a given segment if all of the segments below it are |
| the max (or this is the last segment). This means that we don't have to |
| write those initially, we can just keep the most recent non-maximal |
| segment and a counter of the number of maximal segments. When we reach a |
| non-maximal segment, we write the stored segment as well as as many 9s as |
| are necessary. Alternately, if we reach the end and have to round up, then |
| we round the stored segment, and write zeroes following it. If this |
| crosses the decimal point, then we have to shift it one space to the |
| right. |
| This FloatWriter class does the buffering and counting, and writes to the |
| output when necessary. |
| */ |
| class FloatWriter { |
| char block_buffer[BLOCK_SIZE]; // The buffer that holds a block. |
| size_t buffered_digits = 0; // The number of digits held in the buffer. |
| bool has_written = false; // True once any digits have been output. |
| size_t max_block_count = 0; // The # of blocks of all 9s currently buffered. |
| size_t total_digits = 0; // The number of digits that will be output. |
| size_t digits_before_decimal = 0; // The # of digits to write before the '.' |
| size_t total_digits_written = 0; // The # of digits that have been output. |
| bool has_decimal_point; // True if the number has a decimal point. |
| Writer *writer; // Writes to the final output. |
| PaddingWriter padding_writer; // Handles prefixes/padding, uses total_digits. |
| |
| int flush_buffer() { |
| // Write the most recent buffered block, and mark has_written |
| if (!has_written) { |
| has_written = true; |
| RET_IF_RESULT_NEGATIVE( |
| padding_writer.write_left_padding(writer, total_digits)); |
| } |
| |
| // if the decimal point is the next character, or is in the range covered |
| // by the buffered block, write the appropriate digits and the decimal |
| // point. |
| if (total_digits_written < digits_before_decimal && |
| total_digits_written + buffered_digits >= digits_before_decimal && |
| has_decimal_point) { |
| size_t digits_to_write = digits_before_decimal - total_digits_written; |
| if (digits_to_write > 0) { |
| // Write the digits before the decimal point. |
| RET_IF_RESULT_NEGATIVE(writer->write({block_buffer, digits_to_write})); |
| } |
| RET_IF_RESULT_NEGATIVE(writer->write(DECIMAL_POINT)); |
| if (buffered_digits - digits_to_write > 0) { |
| // Write the digits after the decimal point. |
| RET_IF_RESULT_NEGATIVE( |
| writer->write({block_buffer + digits_to_write, |
| (buffered_digits - digits_to_write)})); |
| } |
| // add 1 for the decimal point |
| total_digits_written += buffered_digits + 1; |
| // Mark the buffer as empty. |
| buffered_digits = 0; |
| } |
| |
| // Clear the buffered digits. |
| if (buffered_digits > 0) { |
| RET_IF_RESULT_NEGATIVE(writer->write({block_buffer, buffered_digits})); |
| total_digits_written += buffered_digits; |
| buffered_digits = 0; |
| } |
| |
| // if the decimal point is the next character, or is in the range covered |
| // by the max blocks, write the appropriate digits and the decimal point. |
| if (total_digits_written < digits_before_decimal && |
| total_digits_written + BLOCK_SIZE * max_block_count >= |
| digits_before_decimal && |
| has_decimal_point) { |
| size_t digits_to_write = digits_before_decimal - total_digits_written; |
| if (digits_to_write > 0) { |
| RET_IF_RESULT_NEGATIVE(writer->write('9', digits_to_write)); |
| } |
| RET_IF_RESULT_NEGATIVE(writer->write(DECIMAL_POINT)); |
| if ((BLOCK_SIZE * max_block_count) - digits_to_write > 0) { |
| RET_IF_RESULT_NEGATIVE(writer->write( |
| '9', (BLOCK_SIZE * max_block_count) - digits_to_write)); |
| } |
| // add 1 for the decimal point |
| total_digits_written += BLOCK_SIZE * max_block_count + 1; |
| // clear the buffer of max blocks |
| max_block_count = 0; |
| } |
| |
| // Clear the buffer of max blocks |
| if (max_block_count > 0) { |
| RET_IF_RESULT_NEGATIVE(writer->write('9', max_block_count * BLOCK_SIZE)); |
| total_digits_written += max_block_count * BLOCK_SIZE; |
| max_block_count = 0; |
| } |
| return 0; |
| } |
| |
| cpp::string_view exp_str(int exponent, cpp::span<char> exp_buffer) { |
| |
| // -exponent will never overflow because all long double types we support |
| // have at most 15 bits of mantissa and the C standard defines an int as |
| // being at least 16 bits. |
| static_assert(fputil::FloatProperties<long double>::EXPONENT_WIDTH < |
| (sizeof(int) * 8)); |
| |
| int positive_exponent = exponent < 0 ? -exponent : exponent; |
| char exp_sign = exponent < 0 ? '-' : '+'; |
| auto const int_to_str = |
| *IntegerToString::dec(positive_exponent, exp_buffer); |
| |
| // IntegerToString writes the digits from right to left so there will be |
| // space to the left of int_to_str. |
| size_t digits_in_exp = int_to_str.size(); |
| size_t index = exp_buffer.size() - digits_in_exp - 1; |
| |
| // Ensure that at least two digits were written. IntegerToString always |
| // writes at least 1 digit (it writes "0" when the input number is 0). |
| if (digits_in_exp < 2) { |
| exp_buffer[index] = '0'; |
| --index; |
| } |
| |
| // Since the exp_buffer has to be sized to handle an intmax_t, it has space |
| // for a sign. In this case we're handling the sign on our own since we also |
| // want plus signs for positive numbers. |
| exp_buffer[index] = exp_sign; |
| |
| return cpp::string_view(exp_buffer.data() + index, |
| exp_buffer.size() - index); |
| } |
| |
| public: |
| FloatWriter(Writer *init_writer, bool init_has_decimal_point, |
| const PaddingWriter &init_padding_writer) |
| : has_decimal_point(init_has_decimal_point), writer(init_writer), |
| padding_writer(init_padding_writer) {} |
| |
| void init(size_t init_total_digits, size_t init_digits_before_decimal) { |
| total_digits = init_total_digits; |
| digits_before_decimal = init_digits_before_decimal; |
| } |
| |
| void write_first_block(BlockInt block, bool exp_format = false) { |
| char buf[IntegerToString::dec_bufsize<intmax_t>()]; |
| auto const int_to_str = *IntegerToString::dec(block, buf); |
| size_t digits_buffered = int_to_str.size(); |
| // Block Buffer is guaranteed to not overflow since block cannot have more |
| // than BLOCK_SIZE digits. |
| // TODO: Replace with memcpy |
| for (size_t count = 0; count < digits_buffered; ++count) { |
| block_buffer[count] = int_to_str[count]; |
| } |
| buffered_digits = digits_buffered; |
| |
| // In the exponent format (%e) we know how many digits will be written even |
| // before calculating any blocks, whereas the decimal format (%f) has to |
| // write all of the blocks that would come before the decimal place. |
| if (!exp_format) { |
| total_digits += digits_buffered; |
| digits_before_decimal += digits_buffered; |
| } |
| } |
| |
| int write_middle_block(BlockInt block) { |
| if (block == MAX_BLOCK) { // Buffer max blocks in case of rounding |
| ++max_block_count; |
| } else { // If a non-max block has been found |
| RET_IF_RESULT_NEGATIVE(flush_buffer()); |
| |
| // Now buffer the current block. We add 1 + MAX_BLOCK to force the |
| // leading zeroes, and drop the leading one. This is probably inefficient, |
| // but it works. See https://xkcd.com/2021/ |
| char buf[IntegerToString::dec_bufsize<intmax_t>()]; |
| auto const int_to_str = |
| *IntegerToString::dec(block + (MAX_BLOCK + 1), buf); |
| // TODO: Replace with memcpy |
| for (size_t count = 0; count < BLOCK_SIZE; ++count) { |
| block_buffer[count] = int_to_str[count + 1]; |
| } |
| |
| buffered_digits = BLOCK_SIZE; |
| } |
| return 0; |
| } |
| |
| int write_last_block_dec(BlockInt block, size_t block_digits, |
| RoundDirection round) { |
| char end_buff[BLOCK_SIZE]; |
| |
| char buf[IntegerToString::dec_bufsize<intmax_t>()]; |
| auto const int_to_str = *IntegerToString::dec(block + (MAX_BLOCK + 1), buf); |
| |
| // copy the last block_digits characters into the start of end_buff. |
| // TODO: Replace with memcpy |
| for (int count = block_digits - 1; count >= 0; --count) { |
| end_buff[count] = int_to_str[count + 1 + (BLOCK_SIZE - block_digits)]; |
| } |
| |
| char low_digit; |
| if (block_digits > 0) { |
| low_digit = end_buff[block_digits - 1]; |
| } else if (max_block_count > 0) { |
| low_digit = '9'; |
| } else { |
| low_digit = block_buffer[buffered_digits - 1]; |
| } |
| |
| // Round up |
| if (round == RoundDirection::Up || |
| (round == RoundDirection::Even && low_digit % 2 != 0)) { |
| bool has_carry = true; |
| // handle the low block that we're adding |
| for (int count = block_digits - 1; count >= 0 && has_carry; --count) { |
| if (end_buff[count] == '9') { |
| end_buff[count] = '0'; |
| } else { |
| end_buff[count] += 1; |
| has_carry = false; |
| } |
| } |
| // handle the high block that's buffered |
| for (int count = buffered_digits - 1; count >= 0 && has_carry; --count) { |
| if (block_buffer[count] == '9') { |
| block_buffer[count] = '0'; |
| } else { |
| block_buffer[count] += 1; |
| has_carry = false; |
| } |
| } |
| |
| // has_carry should only be true here if every previous digit is 9, which |
| // implies that the number has never been written. |
| if (has_carry /* && !has_written */) { |
| ++total_digits; |
| ++digits_before_decimal; |
| // Normally write_left_padding is called by flush_buffer but since we're |
| // rounding up all of the digits, the ones in the buffer are wrong and |
| // can't be flushed. |
| RET_IF_RESULT_NEGATIVE( |
| padding_writer.write_left_padding(writer, total_digits)); |
| // Now we know we need to print a leading 1, zeroes up to the decimal |
| // point, the decimal point, and then finally digits after it. |
| RET_IF_RESULT_NEGATIVE(writer->write('1')); |
| // digits_before_decimal - 1 to account for the leading '1' |
| RET_IF_RESULT_NEGATIVE(writer->write('0', digits_before_decimal - 1)); |
| if (has_decimal_point) { |
| RET_IF_RESULT_NEGATIVE(writer->write(DECIMAL_POINT)); |
| // add one to digits_before_decimal to account for the decimal point |
| // itself. |
| if (total_digits > digits_before_decimal + 1) { |
| RET_IF_RESULT_NEGATIVE( |
| writer->write('0', total_digits - (digits_before_decimal + 1))); |
| } |
| } |
| total_digits_written = total_digits; |
| return 0; |
| } |
| } |
| // Either we intend to round down, or the rounding up is complete. Flush the |
| // buffers. |
| |
| RET_IF_RESULT_NEGATIVE(flush_buffer()); |
| |
| // And then write the final block. |
| RET_IF_RESULT_NEGATIVE(writer->write({end_buff, block_digits})); |
| total_digits_written += block_digits; |
| return 0; |
| } |
| |
| int write_last_block_exp(uint32_t block, size_t block_digits, |
| RoundDirection round, int exponent, char exp_char) { |
| char end_buff[BLOCK_SIZE]; |
| |
| { |
| char buf[IntegerToString::dec_bufsize<intmax_t>()]; |
| auto const int_to_str = |
| *IntegerToString::dec(block + (MAX_BLOCK + 1), buf); |
| |
| // copy the last block_digits characters into the start of end_buff. |
| // TODO: Replace with memcpy |
| for (int count = block_digits - 1; count >= 0; --count) { |
| end_buff[count] = int_to_str[count + 1 + (BLOCK_SIZE - block_digits)]; |
| } |
| } |
| |
| char low_digit; |
| if (block_digits > 0) { |
| low_digit = end_buff[block_digits - 1]; |
| } else if (max_block_count > 0) { |
| low_digit = '9'; |
| } else { |
| low_digit = block_buffer[buffered_digits - 1]; |
| } |
| |
| // Round up |
| if (round == RoundDirection::Up || |
| (round == RoundDirection::Even && low_digit % 2 != 0)) { |
| bool has_carry = true; |
| // handle the low block that we're adding |
| for (int count = block_digits - 1; count >= 0 && has_carry; --count) { |
| if (end_buff[count] == '9') { |
| end_buff[count] = '0'; |
| } else { |
| end_buff[count] += 1; |
| has_carry = false; |
| } |
| } |
| // handle the high block that's buffered |
| for (int count = buffered_digits - 1; count >= 0 && has_carry; --count) { |
| if (block_buffer[count] == '9') { |
| block_buffer[count] = '0'; |
| } else { |
| block_buffer[count] += 1; |
| has_carry = false; |
| } |
| } |
| |
| // has_carry should only be true here if every previous digit is 9, which |
| // implies that the number has never been written. |
| if (has_carry /* && !has_written */) { |
| // Since this is exponential notation, we don't write any more digits |
| // but we do increment the exponent. |
| ++exponent; |
| |
| char buf[IntegerToString::dec_bufsize<intmax_t>()]; |
| auto const int_to_str = exp_str(exponent, buf); |
| |
| // TODO: also change this to calculate the width of the number more |
| // efficiently. |
| size_t exponent_width = int_to_str.size(); |
| size_t number_digits = |
| buffered_digits + (max_block_count * BLOCK_SIZE) + block_digits; |
| |
| // Here we have to recalculate the total number of digits since the |
| // exponent's width may have changed. We're only adding 1 to exponent |
| // width since exp_str appends the sign. |
| total_digits = 1 + number_digits + 1 + exponent_width; |
| |
| // Normally write_left_padding is called by flush_buffer but since we're |
| // rounding up all of the digits, the ones in the buffer are wrong and |
| // can't be flushed. |
| RET_IF_RESULT_NEGATIVE( |
| padding_writer.write_left_padding(writer, total_digits)); |
| // Now we know we need to print a leading 1, the decimal point, and then |
| // zeroes after it. |
| RET_IF_RESULT_NEGATIVE(writer->write('1')); |
| // digits_before_decimal - 1 to account for the leading '1' |
| if (has_decimal_point) { |
| RET_IF_RESULT_NEGATIVE(writer->write(DECIMAL_POINT)); |
| // This is just the length of the number, not including the decimal |
| // point, or exponent. |
| |
| if (number_digits > 1) { |
| RET_IF_RESULT_NEGATIVE(writer->write('0', number_digits - 1)); |
| } |
| } |
| RET_IF_RESULT_NEGATIVE(writer->write(exp_char)); |
| RET_IF_RESULT_NEGATIVE(writer->write(int_to_str)); |
| |
| total_digits_written = total_digits; |
| return WRITE_OK; |
| } |
| } |
| // Either we intend to round down, or the rounding up is complete. Flush the |
| // buffers. |
| |
| RET_IF_RESULT_NEGATIVE(flush_buffer()); |
| |
| // And then write the final block. It's written via the buffer so that if |
| // this is also the first block, the decimal point will be placed correctly. |
| |
| // TODO: Replace with memcpy |
| for (size_t count = 0; count < block_digits; ++count) { |
| block_buffer[count] = end_buff[count]; |
| } |
| buffered_digits = block_digits; |
| RET_IF_RESULT_NEGATIVE(flush_buffer()); |
| |
| char buf[IntegerToString::dec_bufsize<intmax_t>()]; |
| auto const int_to_str = exp_str(exponent, buf); |
| |
| RET_IF_RESULT_NEGATIVE(writer->write(exp_char)); |
| RET_IF_RESULT_NEGATIVE(writer->write(int_to_str)); |
| |
| total_digits_written = total_digits; |
| |
| return WRITE_OK; |
| } |
| |
| int write_zeroes(uint32_t num_zeroes) { |
| RET_IF_RESULT_NEGATIVE(flush_buffer()); |
| RET_IF_RESULT_NEGATIVE(writer->write('0', num_zeroes)); |
| return 0; |
| } |
| |
| int right_pad() { |
| return padding_writer.write_right_padding(writer, total_digits); |
| } |
| }; |
| |
| template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0> |
| LIBC_INLINE int convert_float_decimal_typed(Writer *writer, |
| const FormatSection &to_conv, |
| fputil::FPBits<T> float_bits) { |
| // signed because later we use -MANT_WIDTH |
| constexpr int32_t MANT_WIDTH = fputil::MantissaWidth<T>::VALUE; |
| bool is_negative = float_bits.get_sign(); |
| int exponent = float_bits.get_exponent(); |
| |
| char sign_char = 0; |
| |
| if (is_negative) |
| sign_char = '-'; |
| else if ((to_conv.flags & FormatFlags::FORCE_SIGN) == FormatFlags::FORCE_SIGN) |
| sign_char = '+'; // FORCE_SIGN has precedence over SPACE_PREFIX |
| else if ((to_conv.flags & FormatFlags::SPACE_PREFIX) == |
| FormatFlags::SPACE_PREFIX) |
| sign_char = ' '; |
| |
| // If to_conv doesn't specify a precision, the precision defaults to 6. |
| const size_t precision = to_conv.precision < 0 ? 6 : to_conv.precision; |
| bool has_decimal_point = |
| (precision > 0) || ((to_conv.flags & FormatFlags::ALTERNATE_FORM) != 0); |
| |
| // nonzero is false until a nonzero digit is found. It is used to determine if |
| // leading zeroes should be printed, since before the first digit they are |
| // ignored. |
| bool nonzero = false; |
| |
| PaddingWriter padding_writer(to_conv, sign_char); |
| FloatWriter float_writer(writer, has_decimal_point, padding_writer); |
| FloatToString<T> float_converter(static_cast<T>(float_bits)); |
| |
| const uint32_t positive_blocks = float_converter.get_positive_blocks(); |
| |
| if (positive_blocks >= 0) { |
| // This loop iterates through the number a block at a time until it finds a |
| // block that is not zero or it hits the decimal point. This is because all |
| // zero blocks before the first nonzero digit or the decimal point are |
| // ignored (no leading zeroes, at least at this stage). |
| int32_t i = static_cast<int32_t>(positive_blocks) - 1; |
| for (; i >= 0; --i) { |
| BlockInt digits = float_converter.get_positive_block(i); |
| if (nonzero) { |
| RET_IF_RESULT_NEGATIVE(float_writer.write_middle_block(digits)); |
| } else if (digits != 0) { |
| size_t blocks_before_decimal = i; |
| float_writer.init((blocks_before_decimal * BLOCK_SIZE) + |
| (has_decimal_point ? 1 : 0) + precision, |
| blocks_before_decimal * BLOCK_SIZE); |
| float_writer.write_first_block(digits); |
| |
| nonzero = true; |
| } |
| } |
| } |
| |
| // if we haven't yet found a valid digit, buffer a zero. |
| if (!nonzero) { |
| float_writer.init((has_decimal_point ? 1 : 0) + precision, 0); |
| float_writer.write_first_block(0); |
| } |
| |
| if (exponent < MANT_WIDTH) { |
| const uint32_t blocks = (precision / BLOCK_SIZE) + 1; |
| uint32_t i = 0; |
| // if all the blocks we should write are zero |
| if (blocks <= float_converter.zero_blocks_after_point()) { |
| i = blocks; // just write zeroes up to precision |
| RET_IF_RESULT_NEGATIVE(float_writer.write_zeroes(precision)); |
| } else if (i < float_converter.zero_blocks_after_point()) { |
| // else if there are some blocks that are zeroes |
| i = float_converter.zero_blocks_after_point(); |
| // write those blocks as zeroes. |
| RET_IF_RESULT_NEGATIVE(float_writer.write_zeroes(9 * i)); |
| } |
| // for each unwritten block |
| for (; i < blocks; ++i) { |
| if (float_converter.is_lowest_block(i)) { |
| const uint32_t fill = precision - 9 * i; |
| RET_IF_RESULT_NEGATIVE(float_writer.write_zeroes(fill)); |
| break; |
| } |
| BlockInt digits = float_converter.get_negative_block(i); |
| if (i < blocks - 1) { |
| RET_IF_RESULT_NEGATIVE(float_writer.write_middle_block(digits)); |
| } else { |
| |
| const uint32_t maximum = precision - BLOCK_SIZE * i; |
| uint32_t lastDigit = 0; |
| for (uint32_t k = 0; k < BLOCK_SIZE - maximum; ++k) { |
| lastDigit = digits % 10; |
| digits /= 10; |
| } |
| RoundDirection round; |
| // Is m * 10^(additionalDigits + 1) / 2^(-exponent) integer? |
| const int32_t requiredTwos = |
| -exponent - MANT_WIDTH - (int32_t)precision - 1; |
| const bool trailingZeros = |
| requiredTwos <= 0 || |
| (requiredTwos < 60 && |
| multiple_of_power_of_2(float_bits.get_explicit_mantissa(), |
| (uint32_t)requiredTwos)); |
| switch (fputil::get_round()) { |
| case FE_TONEAREST: |
| // Round to nearest, if it's exactly halfway then round to even. |
| if (lastDigit != 5) { |
| round = lastDigit > 5 ? RoundDirection::Up : RoundDirection::Down; |
| } else { |
| round = trailingZeros ? RoundDirection::Even : RoundDirection::Up; |
| } |
| break; |
| case FE_DOWNWARD: |
| if (is_negative && (!trailingZeros || lastDigit > 0)) { |
| round = RoundDirection::Up; |
| } else { |
| round = RoundDirection::Down; |
| } |
| break; |
| case FE_UPWARD: |
| if (!is_negative && (!trailingZeros || lastDigit > 0)) { |
| round = RoundDirection::Up; |
| } else { |
| round = RoundDirection::Down; |
| } |
| round = is_negative ? RoundDirection::Down : RoundDirection::Up; |
| break; |
| case FE_TOWARDZERO: |
| round = RoundDirection::Down; |
| break; |
| } |
| RET_IF_RESULT_NEGATIVE( |
| float_writer.write_last_block_dec(digits, maximum, round)); |
| break; |
| } |
| } |
| } else { |
| RET_IF_RESULT_NEGATIVE(float_writer.write_zeroes(precision)); |
| } |
| RET_IF_RESULT_NEGATIVE(float_writer.right_pad()); |
| return WRITE_OK; |
| } |
| |
| template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0> |
| LIBC_INLINE int convert_float_dec_exp_typed(Writer *writer, |
| const FormatSection &to_conv, |
| fputil::FPBits<T> float_bits) { |
| // signed because later we use -MANT_WIDTH |
| constexpr int32_t MANT_WIDTH = fputil::MantissaWidth<T>::VALUE; |
| bool is_negative = float_bits.get_sign(); |
| int exponent = float_bits.get_exponent(); |
| MantissaInt mantissa = float_bits.get_explicit_mantissa(); |
| |
| const char a = (to_conv.conv_name & 32) | 'A'; |
| |
| char sign_char = 0; |
| |
| if (is_negative) |
| sign_char = '-'; |
| else if ((to_conv.flags & FormatFlags::FORCE_SIGN) == FormatFlags::FORCE_SIGN) |
| sign_char = '+'; // FORCE_SIGN has precedence over SPACE_PREFIX |
| else if ((to_conv.flags & FormatFlags::SPACE_PREFIX) == |
| FormatFlags::SPACE_PREFIX) |
| sign_char = ' '; |
| |
| // If to_conv doesn't specify a precision, the precision defaults to 6. |
| const size_t precision = to_conv.precision < 0 ? 6 : to_conv.precision; |
| bool has_decimal_point = |
| (precision > 0) || ((to_conv.flags & FormatFlags::ALTERNATE_FORM) != 0); |
| |
| PaddingWriter padding_writer(to_conv, sign_char); |
| FloatWriter float_writer(writer, has_decimal_point, padding_writer); |
| FloatToString<T> float_converter(static_cast<T>(float_bits)); |
| |
| size_t digits_written = 0; |
| int final_exponent = 0; |
| |
| // Here we would subtract 1 to account for the fact that block 0 counts as a |
| // positive block, but the loop below accounts for this by starting with |
| // subtracting 1 from cur_block. |
| int cur_block; |
| |
| if (exponent < 0) { |
| cur_block = -float_converter.zero_blocks_after_point(); |
| } else { |
| cur_block = float_converter.get_positive_blocks(); |
| } |
| |
| BlockInt digits = 0; |
| |
| // If the mantissa is 0, then the number is 0, meaning that looping until a |
| // non-zero block is found will loop forever. The first block is just 0. |
| if (mantissa != 0) { |
| // This loop finds the first block. |
| while (digits == 0) { |
| --cur_block; |
| digits = float_converter.get_block(cur_block); |
| } |
| } else { |
| cur_block = 0; |
| } |
| |
| // TODO: Find a better way to calculate the number of digits in the |
| // initial block and exponent. |
| char buf[IntegerToString::dec_bufsize<intmax_t>()]; |
| auto int_to_str = *IntegerToString::dec(digits, buf); |
| size_t block_width = int_to_str.size(); |
| |
| final_exponent = (cur_block * BLOCK_SIZE) + (block_width - 1); |
| int positive_exponent = final_exponent < 0 ? -final_exponent : final_exponent; |
| |
| int_to_str = *IntegerToString::dec(positive_exponent, buf); |
| size_t exponent_width = int_to_str.size(); |
| |
| // Calculate the total number of digits in the number. |
| // 1 - the digit before the decimal point |
| // 1 - the decimal point (optional) |
| // precision - the number of digits after the decimal point |
| // 1 - the 'e' at the start of the exponent |
| // 1 - the sign at the start of the exponent |
| // max(2, exp width) - the digits of the exponent, min 2. |
| |
| float_writer.init(1 + (has_decimal_point ? 1 : 0) + precision + 2 + |
| (exponent_width < 2 ? 2 : exponent_width), |
| 1); |
| |
| // If this block is not the last block |
| if (block_width <= precision + 1) { |
| float_writer.write_first_block(digits, true); |
| digits_written += block_width; |
| --cur_block; |
| } |
| |
| // For each middle block. |
| for (; digits_written + BLOCK_SIZE < precision + 1; --cur_block) { |
| digits = float_converter.get_block(cur_block); |
| |
| RET_IF_RESULT_NEGATIVE(float_writer.write_middle_block(digits)); |
| digits_written += BLOCK_SIZE; |
| } |
| |
| digits = float_converter.get_block(cur_block); |
| |
| size_t last_block_size = BLOCK_SIZE; |
| |
| // if the last block is also the first block, then ignore leading zeroes. |
| if (digits_written == 0) { |
| // TODO: Find a better way to calculate the number of digits in a block. |
| char buf[IntegerToString::dec_bufsize<intmax_t>()]; |
| auto int_to_str = *IntegerToString::dec(digits, buf); |
| last_block_size = int_to_str.size(); |
| } |
| |
| // This is the last block. |
| const uint32_t maximum = precision + 1 - digits_written; |
| uint32_t lastDigit = 0; |
| for (uint32_t k = 0; k < last_block_size - maximum; ++k) { |
| lastDigit = digits % 10; |
| digits /= 10; |
| } |
| RoundDirection round; |
| // Is m * 10^(additionalDigits + 1) / 2^(-exponent) integer? |
| const int32_t requiredTwos = -exponent - MANT_WIDTH - (int32_t)precision - 1; |
| const bool trailingZeros = |
| requiredTwos <= 0 || |
| (requiredTwos < 60 && |
| multiple_of_power_of_2(float_bits.get_explicit_mantissa(), |
| (uint32_t)requiredTwos)); |
| switch (fputil::get_round()) { |
| case FE_TONEAREST: |
| // Round to nearest, if it's exactly halfway then round to even. |
| if (lastDigit != 5) { |
| round = lastDigit > 5 ? RoundDirection::Up : RoundDirection::Down; |
| } else { |
| round = trailingZeros ? RoundDirection::Even : RoundDirection::Up; |
| } |
| break; |
| case FE_DOWNWARD: |
| if (is_negative && (!trailingZeros || lastDigit > 0)) { |
| round = RoundDirection::Up; |
| } else { |
| round = RoundDirection::Down; |
| } |
| break; |
| case FE_UPWARD: |
| if (!is_negative && (!trailingZeros || lastDigit > 0)) { |
| round = RoundDirection::Up; |
| } else { |
| round = RoundDirection::Down; |
| } |
| round = is_negative ? RoundDirection::Down : RoundDirection::Up; |
| break; |
| case FE_TOWARDZERO: |
| round = RoundDirection::Down; |
| break; |
| } |
| RET_IF_RESULT_NEGATIVE(float_writer.write_last_block_exp( |
| digits, maximum, round, final_exponent, a + 'E' - 'A')); |
| |
| RET_IF_RESULT_NEGATIVE(float_writer.right_pad()); |
| return WRITE_OK; |
| } |
| |
| LIBC_INLINE int convert_float_decimal(Writer *writer, |
| const FormatSection &to_conv) { |
| if (to_conv.length_modifier == LengthModifier::L) { |
| fputil::FPBits<long double>::UIntType float_raw = to_conv.conv_val_raw; |
| fputil::FPBits<long double> float_bits(float_raw); |
| if (!float_bits.is_inf_or_nan()) { |
| return convert_float_decimal_typed<long double>(writer, to_conv, |
| float_bits); |
| } |
| } else { |
| fputil::FPBits<double>::UIntType float_raw = to_conv.conv_val_raw; |
| fputil::FPBits<double> float_bits(float_raw); |
| if (!float_bits.is_inf_or_nan()) { |
| return convert_float_decimal_typed<double>(writer, to_conv, float_bits); |
| } |
| } |
| |
| return convert_inf_nan(writer, to_conv); |
| } |
| |
| LIBC_INLINE int convert_float_dec_exp(Writer *writer, |
| const FormatSection &to_conv) { |
| if (to_conv.length_modifier == LengthModifier::L) { |
| fputil::FPBits<long double>::UIntType float_raw = to_conv.conv_val_raw; |
| fputil::FPBits<long double> float_bits(float_raw); |
| if (!float_bits.is_inf_or_nan()) { |
| return convert_float_dec_exp_typed<long double>(writer, to_conv, |
| float_bits); |
| } |
| } else { |
| fputil::FPBits<double>::UIntType float_raw = to_conv.conv_val_raw; |
| fputil::FPBits<double> float_bits(float_raw); |
| if (!float_bits.is_inf_or_nan()) { |
| return convert_float_dec_exp_typed<double>(writer, to_conv, float_bits); |
| } |
| } |
| |
| return convert_inf_nan(writer, to_conv); |
| } |
| |
| } // namespace printf_core |
| } // namespace __llvm_libc |
| |
| #endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_FLOAT_DEC_CONVERTER_H |