//===-- Fixed Point 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_FIXED_CONVERTER_H #define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_FIXED_CONVERTER_H #include "include/llvm-libc-macros/stdfix-macros.h" #include "src/__support/CPP/string_view.h" #include "src/__support/fixed_point/fx_bits.h" #include "src/__support/fixed_point/fx_rep.h" #include "src/__support/integer_to_string.h" #include "src/__support/libc_assert.h" #include "src/stdio/printf_core/converter_utils.h" #include "src/stdio/printf_core/core_structs.h" #include "src/stdio/printf_core/writer.h" #include #include namespace LIBC_NAMESPACE { namespace printf_core { // This is just for assertions. It will be compiled out for release builds. LIBC_INLINE constexpr uint32_t const_ten_exp(uint32_t exponent) { uint32_t result = 1; LIBC_ASSERT(exponent < 11); for (uint32_t i = 0; i < exponent; ++i) result *= 10; return result; } #define READ_FX_BITS(TYPE) \ do { \ auto fixed_bits = fixed_point::FXBits( \ fixed_point::FXRep::StorageType(to_conv.conv_val_raw)); \ integral = fixed_bits.get_integral(); \ fractional = fixed_bits.get_fraction(); \ exponent = fixed_bits.get_exponent(); \ is_negative = fixed_bits.get_sign(); \ } while (false) #define APPLY_FX_LENGTH_MODIFIER(LENGTH_MODIFIER) \ do { \ if (to_conv.conv_name == 'r') { \ READ_FX_BITS(LENGTH_MODIFIER fract); \ } else if (to_conv.conv_name == 'R') { \ READ_FX_BITS(unsigned LENGTH_MODIFIER fract); \ } else if (to_conv.conv_name == 'k') { \ READ_FX_BITS(LENGTH_MODIFIER accum); \ } else if (to_conv.conv_name == 'K') { \ READ_FX_BITS(unsigned LENGTH_MODIFIER accum); \ } else { \ LIBC_ASSERT(false && "Invalid conversion name passed to convert_fixed"); \ return FIXED_POINT_CONVERSION_ERROR; \ } \ } while (false) LIBC_INLINE int convert_fixed(Writer *writer, const FormatSection &to_conv) { // Long accum should be the largest type, so we can store all the smaller // numbers in things sized for it. using LARep = fixed_point::FXRep; using StorageType = LARep::StorageType; // All of the letters will be defined relative to variable a, which will be // the appropriate case based on the name of the conversion. This converts any // conversion name into the letter 'a' with the appropriate case. const char a = (to_conv.conv_name & 32) | 'A'; FormatFlags flags = to_conv.flags; bool is_negative; int exponent; StorageType integral; StorageType fractional; // r = fract // k = accum // lowercase = signed // uppercase = unsigned // h = short // l = long // any other length modifier has no effect if (to_conv.length_modifier == LengthModifier::h) { APPLY_FX_LENGTH_MODIFIER(short); } else if (to_conv.length_modifier == LengthModifier::l) { APPLY_FX_LENGTH_MODIFIER(long); } else { APPLY_FX_LENGTH_MODIFIER(); } LIBC_ASSERT(static_cast(exponent) <= (sizeof(StorageType) - sizeof(uint32_t)) * CHAR_BIT && "StorageType must be large enough to hold the fractional " "component multiplied by a 32 bit number."); // 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) || ((flags & FormatFlags::ALTERNATE_FORM) != 0); // The number of non-zero digits below the decimal point for a negative power // of 2 in base 10 is equal to the magnitude of the power of 2. // A quick proof: // Let p be any positive integer. // Let e = 2^(-p) // Let t be a positive integer such that e * 10^t is an integer. // By definition: The smallest allowed value of t must be equal to the number // of non-zero digits below the decimal point in e. // If we evaluate e * 10^t we get the following: // e * 10^t = 2^(-p) * 10*t = 2^(-p) * 2^t * 5^t = 5^t * 2^(t-p) // For 5^t * 2^(t-p) to be an integer, both exponents must be non-negative, // since 5 and 2 are coprime. // The smallest value of t such that t-p is non-negative is p. // Therefor, the number of non-zero digits below the decimal point for a given // negative power of 2 "p" is equal to the value of p. constexpr size_t MAX_FRACTION_DIGITS = LARep::FRACTION_LEN; char fraction_digits[MAX_FRACTION_DIGITS]; size_t valid_fraction_digits = 0; // TODO: Factor this part out while (fractional > 0) { uint32_t cur_digits = 0; // 10^9 is used since it's the largest power of 10 that fits in a uint32_t constexpr uint32_t TEN_EXP_NINE = 1000000000; constexpr size_t DIGITS_PER_BLOCK = 9; // Multiply by 10^9, then grab the digits above the decimal point, then // clear those digits in fractional. fractional = fractional * TEN_EXP_NINE; cur_digits = static_cast(fractional >> exponent); fractional = fractional % (StorageType(1) << exponent); // we add TEN_EXP_NINE to force leading zeroes to show up, then we skip the // first digit in the loop. const IntegerToString cur_fractional_digits(cur_digits + TEN_EXP_NINE); for (size_t i = 0; i < DIGITS_PER_BLOCK && valid_fraction_digits < MAX_FRACTION_DIGITS; ++i, ++valid_fraction_digits) fraction_digits[valid_fraction_digits] = cur_fractional_digits.view()[i + 1]; if (valid_fraction_digits >= MAX_FRACTION_DIGITS) { LIBC_ASSERT(fractional == 0 && "If the fraction digit buffer is full, " "there should be no remaining digits."); /* A visual explanation of what this assert is checking: 32 digits (max for 32 bit fract) +------------------------------++--+--- must be zero | || | 123456789012345678901234567890120000 | || || || | +-------++-------++-------++-------+ 9 digit blocks */ LIBC_ASSERT(cur_digits % const_ten_exp( DIGITS_PER_BLOCK - (MAX_FRACTION_DIGITS % DIGITS_PER_BLOCK)) == 0 && "Digits after the MAX_FRACTION_DIGITS should all be zero."); valid_fraction_digits = MAX_FRACTION_DIGITS; } } if (precision < valid_fraction_digits) { // Handle rounding. Just do round to nearest, tie to even since it's // unspecified. RoundDirection round; char first_digit_after = fraction_digits[precision]; if (first_digit_after > '5') { round = RoundDirection::Up; } else if (first_digit_after < '5') { round = RoundDirection::Down; } else { // first_digit_after == '5' // need to check the remaining digits, but default to even. round = RoundDirection::Even; for (size_t cur_digit_index = precision + 1; cur_digit_index + 1 < valid_fraction_digits; ++cur_digit_index) { if (fraction_digits[cur_digit_index] != '0') { round = RoundDirection::Up; break; } } } // If we need to actually perform rounding, do so. if (round == RoundDirection::Up || round == RoundDirection::Even) { bool keep_rounding = true; int digit_to_round = static_cast(precision) - 1; for (; digit_to_round >= 0 && keep_rounding; --digit_to_round) { keep_rounding = false; char cur_digit = fraction_digits[digit_to_round]; // if the digit should not be rounded up if (round == RoundDirection::Even && ((cur_digit - '0') % 2) == 0) { // break out of the loop break; } fraction_digits[digit_to_round] += 1; // if the digit was a 9, instead replace with a 0. if (cur_digit == '9') { fraction_digits[digit_to_round] = '0'; keep_rounding = true; } } // if every digit below the decimal point was rounded up but we need to // keep rounding if (keep_rounding && (round == RoundDirection::Up || (round == RoundDirection::Even && ((integral % 2) == 1)))) { // add one to the integral portion to round it up. ++integral; } } valid_fraction_digits = precision; } const IntegerToString integral_str(integral); // these are signed to prevent underflow due to negative values. The // eventual values will always be non-negative. size_t trailing_zeroes = 0; int padding; // If the precision is greater than the actual result, pad with 0s if (precision > valid_fraction_digits) trailing_zeroes = precision - (valid_fraction_digits); constexpr cpp::string_view DECIMAL_POINT("."); char sign_char = 0; // Check if the conv name is uppercase if (a == 'A') { // These flags are only for signed conversions, so this removes them if the // conversion is unsigned. flags = FormatFlags(flags & ~(FormatFlags::FORCE_SIGN | FormatFlags::SPACE_PREFIX)); } if (is_negative) sign_char = '-'; else if ((flags & FormatFlags::FORCE_SIGN) == FormatFlags::FORCE_SIGN) sign_char = '+'; // FORCE_SIGN has precedence over SPACE_PREFIX else if ((flags & FormatFlags::SPACE_PREFIX) == FormatFlags::SPACE_PREFIX) sign_char = ' '; padding = static_cast(to_conv.min_width - (sign_char > 0 ? 1 : 0) - integral_str.size() - static_cast(has_decimal_point) - valid_fraction_digits - trailing_zeroes); if (padding < 0) padding = 0; if ((flags & FormatFlags::LEFT_JUSTIFIED) == FormatFlags::LEFT_JUSTIFIED) { // The pattern is (sign), integral, (.), (fraction), (zeroes), (spaces) if (sign_char > 0) RET_IF_RESULT_NEGATIVE(writer->write(sign_char)); RET_IF_RESULT_NEGATIVE(writer->write(integral_str.view())); if (has_decimal_point) RET_IF_RESULT_NEGATIVE(writer->write(DECIMAL_POINT)); if (valid_fraction_digits > 0) RET_IF_RESULT_NEGATIVE( writer->write({fraction_digits, valid_fraction_digits})); if (trailing_zeroes > 0) RET_IF_RESULT_NEGATIVE(writer->write('0', trailing_zeroes)); if (padding > 0) RET_IF_RESULT_NEGATIVE(writer->write(' ', padding)); } else { // The pattern is (spaces), (sign), (zeroes), integral, (.), (fraction), // (zeroes) if ((padding > 0) && ((flags & FormatFlags::LEADING_ZEROES) != FormatFlags::LEADING_ZEROES)) RET_IF_RESULT_NEGATIVE(writer->write(' ', padding)); if (sign_char > 0) RET_IF_RESULT_NEGATIVE(writer->write(sign_char)); if ((padding > 0) && ((flags & FormatFlags::LEADING_ZEROES) == FormatFlags::LEADING_ZEROES)) RET_IF_RESULT_NEGATIVE(writer->write('0', padding)); RET_IF_RESULT_NEGATIVE(writer->write(integral_str.view())); if (has_decimal_point) RET_IF_RESULT_NEGATIVE(writer->write(DECIMAL_POINT)); if (valid_fraction_digits > 0) RET_IF_RESULT_NEGATIVE( writer->write({fraction_digits, valid_fraction_digits})); if (trailing_zeroes > 0) RET_IF_RESULT_NEGATIVE(writer->write('0', trailing_zeroes)); } return WRITE_OK; } } // namespace printf_core } // namespace LIBC_NAMESPACE #endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_FIXED_CONVERTER_H