309 changes: 309 additions & 0 deletions libc/src/stdio/printf_core/fixed_converter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,309 @@
//===-- 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 <inttypes.h>
#include <stddef.h>

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<TYPE>( \
fixed_point::FXRep<TYPE>::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<unsigned long accum>;
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<size_t>(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<uint32_t>(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<uint32_t> 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<int>(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<StorageType> 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<int>(to_conv.min_width - (sign_char > 0 ? 1 : 0) -
integral_str.size() -
static_cast<int>(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
3 changes: 0 additions & 3 deletions libc/src/stdio/printf_core/float_dec_converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,6 @@ constexpr uint32_t MAX_BLOCK = 999999999;
// 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 };

LIBC_INLINE RoundDirection get_round_direction(int last_digit, bool truncated,
fputil::Sign sign) {
switch (fputil::quick_get_round()) {
Expand Down
65 changes: 65 additions & 0 deletions libc/src/stdio/printf_core/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,19 @@
#ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_PARSER_H
#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_PARSER_H

#include "include/llvm-libc-macros/stdfix-macros.h"
#include "src/__support/CPP/optional.h"
#include "src/__support/CPP/type_traits.h"
#include "src/__support/str_to_integer.h"
#include "src/stdio/printf_core/core_structs.h"
#include "src/stdio/printf_core/printf_config.h"

#include <stddef.h>

#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
#include "src/__support/fixed_point/fx_rep.h"
#endif // LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT

namespace LIBC_NAMESPACE {
namespace printf_core {

Expand All @@ -28,6 +34,14 @@ template <> struct int_type_of<double> {
template <> struct int_type_of<long double> {
using type = fputil::FPBits<long double>::StorageType;
};

#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
template <typename T>
struct int_type_of<cpp::enable_if<cpp::is_fixed_point_v<T>, T>> {
using type = typename fixed_point::FXRep<T>::StorageType;
};
#endif // LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT

template <typename T> using int_type_of_v = typename int_type_of<T>::type;

#ifndef LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
Expand Down Expand Up @@ -206,6 +220,25 @@ template <typename ArgProvider> class Parser {
}
break;
#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
// Capitalization represents sign, but we only need to get the right
// bitwidth here so we ignore that.
case ('r'):
case ('R'):
// all fract sizes we support are less than 32 bits, and currently doing
// va_args with fixed point types just doesn't work.
// TODO: Move to fixed point types once va_args supports it.
WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, uint32_t, conv_index);
break;
case ('k'):
case ('K'):
if (lm == LengthModifier::l) {
WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, uint64_t, conv_index);
} else {
WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, uint32_t, conv_index);
}
break;
#endif // LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
#ifndef LIBC_COPT_PRINTF_DISABLE_WRITE_INT
case ('n'):
#endif // LIBC_COPT_PRINTF_DISABLE_WRITE_INT
Expand Down Expand Up @@ -399,6 +432,22 @@ template <typename ArgProvider> class Parser {
else if (cur_type_desc == type_desc_from_type<long double>())
args_cur.template next_var<long double>();
#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
// Floating point numbers may be stored separately from the other
// arguments.
else if (cur_type_desc == type_desc_from_type<short fract>())
args_cur.template next_var<short fract>();
else if (cur_type_desc == type_desc_from_type<fract>())
args_cur.template next_var<fract>();
else if (cur_type_desc == type_desc_from_type<long fract>())
args_cur.template next_var<long fract>();
else if (cur_type_desc == type_desc_from_type<short accum>())
args_cur.template next_var<short accum>();
else if (cur_type_desc == type_desc_from_type<accum>())
args_cur.template next_var<accum>();
else if (cur_type_desc == type_desc_from_type<long accum>())
args_cur.template next_var<long accum>();
#endif // LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
// pointers may be stored separately from normal values.
else if (cur_type_desc == type_desc_from_type<void *>())
args_cur.template next_var<void *>();
Expand Down Expand Up @@ -528,6 +577,22 @@ template <typename ArgProvider> class Parser {
conv_size = type_desc_from_type<long double>();
break;
#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
// Capitalization represents sign, but we only need to get the right
// bitwidth here so we ignore that.
case ('r'):
case ('R'):
conv_size = type_desc_from_type<uint32_t>();
break;
case ('k'):
case ('K'):
if (lm == LengthModifier::l) {
conv_size = type_desc_from_type<uint64_t>();
} else {
conv_size = type_desc_from_type<uint32_t>();
}
break;
#endif // LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
#ifndef LIBC_COPT_PRINTF_DISABLE_WRITE_INT
case ('n'):
#endif // LIBC_COPT_PRINTF_DISABLE_WRITE_INT
Expand Down
7 changes: 7 additions & 0 deletions libc/src/stdio/printf_core/printf_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@
#define LIBC_COPT_PRINTF_INDEX_ARR_LEN 128
#endif

// If fixed point is available and the user hasn't explicitly opted out, then
// enable fixed point.
#if defined(LIBC_COMPILER_HAS_FIXED_POINT) && \
!defined(LIBC_COPT_PRINTF_DISABLE_FIXED_POINT)
#define LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
#endif

// TODO(michaelrj): Provide a proper interface for these options.
// LIBC_COPT_FLOAT_TO_STR_USE_MEGA_LONG_DOUBLE_TABLE
// LIBC_COPT_FLOAT_TO_STR_USE_DYADIC_FLOAT
Expand Down
3 changes: 3 additions & 0 deletions libc/test/src/stdio/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ endif()
if(LIBC_CONF_PRINTF_DISABLE_WRITE_INT)
list(APPEND sprintf_test_copts "-DLIBC_COPT_PRINTF_DISABLE_WRITE_INT")
endif()
if(LIBC_CONF_PRINTF_DISABLE_FIXED_POINT)
list(APPEND sprintf_test_copts "-DLIBC_COPT_PRINTF_DISABLE_FIXED_POINT")
endif()

add_fp_unittest(
sprintf_test
Expand Down
211 changes: 211 additions & 0 deletions libc/test/src/stdio/sprintf_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3201,6 +3201,217 @@ TEST_F(LlvmLibcSPrintfTest, FloatAutoLongDoubleConv) {

#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT

#if defined(LIBC_COMPILER_HAS_FIXED_POINT) && \
!defined(LIBC_COPT_PRINTF_DISABLE_FIXED_POINT)
TEST_F(LlvmLibcSPrintfTest, FixedConv) {

// These numeric tests are potentially a little weak, but the fuzz test is
// more thorough than my handwritten tests tend to be.

// TODO: Replace hex literals with their appropriate fixed point literals.

written = LIBC_NAMESPACE::sprintf(buff, "%k", 0x0); // 0.0
ASSERT_STREQ_LEN(written, buff, "0.000000");

written = LIBC_NAMESPACE::sprintf(buff, "%k", 0x80000000); // -0.0
ASSERT_STREQ_LEN(written, buff, "-0.000000");

written = LIBC_NAMESPACE::sprintf(buff, "%r", 0xffff); // -fract max
ASSERT_STREQ_LEN(written, buff, "-0.999969");

written = LIBC_NAMESPACE::sprintf(buff, "%R", 0xffff); // unsigned fract max
ASSERT_STREQ_LEN(written, buff, "0.999985");

written = LIBC_NAMESPACE::sprintf(buff, "%k", 0xffffffff); // -accum max
ASSERT_STREQ_LEN(written, buff, "-65535.999969");

written =
LIBC_NAMESPACE::sprintf(buff, "%K", 0xffffffff); // unsigned accum max
ASSERT_STREQ_LEN(written, buff, "65535.999985");

written = LIBC_NAMESPACE::sprintf(buff, "%r", 0x7fff); // fract max
ASSERT_STREQ_LEN(written, buff, "0.999969");

written = LIBC_NAMESPACE::sprintf(buff, "%k", 0x7fffffff); // accum max
ASSERT_STREQ_LEN(written, buff, "65535.999969");

// Length Modifier Tests.

written = LIBC_NAMESPACE::sprintf(buff, "%hk", 0x0); // 0.0
ASSERT_STREQ_LEN(written, buff, "0.000000");

written = LIBC_NAMESPACE::sprintf(buff, "%hk", 0xffff); // -short accum max
ASSERT_STREQ_LEN(written, buff, "-255.992188");

written = LIBC_NAMESPACE::sprintf(buff, "%hr", 0x0); // 0.0
ASSERT_STREQ_LEN(written, buff, "0.000000");

written = LIBC_NAMESPACE::sprintf(buff, "%hr", 0xff); // -short fract max
ASSERT_STREQ_LEN(written, buff, "-0.992188");

written = LIBC_NAMESPACE::sprintf(buff, "%hK", 0x0); // 0.0
ASSERT_STREQ_LEN(written, buff, "0.000000");

written =
LIBC_NAMESPACE::sprintf(buff, "%hK", 0xffff); // unsigned short accum max
ASSERT_STREQ_LEN(written, buff, "255.996094");

written = LIBC_NAMESPACE::sprintf(buff, "%hR", 0x0); // 0.0
ASSERT_STREQ_LEN(written, buff, "0.000000");

written =
LIBC_NAMESPACE::sprintf(buff, "%hR", 0xff); // unsigned short fract max
ASSERT_STREQ_LEN(written, buff, "0.996094");

written = LIBC_NAMESPACE::sprintf(buff, "%lk", 0x0); // 0.0
ASSERT_STREQ_LEN(written, buff, "0.000000");

written = LIBC_NAMESPACE::sprintf(buff, "%lk",
0xffffffffffffffff); //-long accum max
ASSERT_STREQ_LEN(written, buff, "-4294967296.000000");

written = LIBC_NAMESPACE::sprintf(buff, "%lr", 0x0); // 0.0
ASSERT_STREQ_LEN(written, buff, "0.000000");

written = LIBC_NAMESPACE::sprintf(buff, "%lr",
0xffffffff); //-long fract max
ASSERT_STREQ_LEN(written, buff, "-1.000000");

written = LIBC_NAMESPACE::sprintf(buff, "%lK", 0x0); // 0.0
ASSERT_STREQ_LEN(written, buff, "0.000000");

written =
LIBC_NAMESPACE::sprintf(buff, "%lK",
0xffffffffffffffff); // unsigned long accum max
ASSERT_STREQ_LEN(written, buff, "4294967296.000000");

written = LIBC_NAMESPACE::sprintf(buff, "%lR", 0x0); // 0.0
ASSERT_STREQ_LEN(written, buff, "0.000000");

written = LIBC_NAMESPACE::sprintf(buff, "%lR",
0xffffffff); // unsigned long fract max
ASSERT_STREQ_LEN(written, buff, "1.000000");

// Min Width Tests.

written = LIBC_NAMESPACE::sprintf(buff, "%10k", 0x0000a000); // 1.25
ASSERT_STREQ_LEN(written, buff, " 1.250000");

written = LIBC_NAMESPACE::sprintf(buff, "%10k", 0x8000a000); //-1.25
ASSERT_STREQ_LEN(written, buff, " -1.250000");

written = LIBC_NAMESPACE::sprintf(buff, "%8k", 0x0000a000); // 1.25
ASSERT_STREQ_LEN(written, buff, "1.250000");

written = LIBC_NAMESPACE::sprintf(buff, "%9k", 0x8000a000); //-1.25
ASSERT_STREQ_LEN(written, buff, "-1.250000");

written = LIBC_NAMESPACE::sprintf(buff, "%4k", 0x0000a000); // 1.25
ASSERT_STREQ_LEN(written, buff, "1.250000");

written = LIBC_NAMESPACE::sprintf(buff, "%4k", 0x8000a000); //-1.25
ASSERT_STREQ_LEN(written, buff, "-1.250000");

// Precision Tests.

written =
LIBC_NAMESPACE::sprintf(buff, "%.16K", 0xFFFFFFFF); // unsigned accum max
ASSERT_STREQ_LEN(written, buff, "65535.9999847412109375");

written = LIBC_NAMESPACE::sprintf(
buff, "%.32lK", 0xFFFFFFFFFFFFFFFF); // unsigned long accum max
ASSERT_STREQ_LEN(written, buff,
"4294967295.99999999976716935634613037109375");

written =
LIBC_NAMESPACE::sprintf(buff, "%.0K", 0xFFFFFFFF); // unsigned accum max
ASSERT_STREQ_LEN(written, buff, "65536");

written = LIBC_NAMESPACE::sprintf(buff, "%.0R", 0xFFFF); // unsigned fract max
ASSERT_STREQ_LEN(written, buff, "1");

// Flag Tests.

written = LIBC_NAMESPACE::sprintf(buff, "%+k", 0x0000a000); // 1.25
ASSERT_STREQ_LEN(written, buff, "+1.250000");

written = LIBC_NAMESPACE::sprintf(buff, "%+k", 0x8000a000); //-1.25
ASSERT_STREQ_LEN(written, buff, "-1.250000");

written = LIBC_NAMESPACE::sprintf(buff, "% k", 0x0000a000); // 1.25
ASSERT_STREQ_LEN(written, buff, " 1.250000");

written = LIBC_NAMESPACE::sprintf(buff, "% k", 0x8000a000); //-1.25
ASSERT_STREQ_LEN(written, buff, "-1.250000");

// unsigned variants ignore sign flags.
written = LIBC_NAMESPACE::sprintf(buff, "%+K", 0x00014000); // 1.25
ASSERT_STREQ_LEN(written, buff, "1.250000");

written = LIBC_NAMESPACE::sprintf(buff, "% K", 0x00014000); // 1.25
ASSERT_STREQ_LEN(written, buff, "1.250000");

written = LIBC_NAMESPACE::sprintf(buff, "%-10k", 0x0000c000); // 1.5
ASSERT_STREQ_LEN(written, buff, "1.500000 ");

written = LIBC_NAMESPACE::sprintf(buff, "%#.k", 0x00008000); // 1.0
ASSERT_STREQ_LEN(written, buff, "1.");

written = LIBC_NAMESPACE::sprintf(buff, "%#.0k", 0x0000c000); // 1.5
ASSERT_STREQ_LEN(written, buff, "2.");

written = LIBC_NAMESPACE::sprintf(buff, "%010k", 0x0000c000); // 1.5
ASSERT_STREQ_LEN(written, buff, "001.500000");

written = LIBC_NAMESPACE::sprintf(buff, "%010k", 0x8000c000); //-1.5
ASSERT_STREQ_LEN(written, buff, "-01.500000");

written = LIBC_NAMESPACE::sprintf(buff, "%+- #0k", 0); // 0.0
ASSERT_STREQ_LEN(written, buff, "+0.000000");

// Combined Tests.

written = LIBC_NAMESPACE::sprintf(buff, "%10.2k", 0x0004feb8); // 9.99
ASSERT_STREQ_LEN(written, buff, " 9.99");

written = LIBC_NAMESPACE::sprintf(buff, "%5.1k", 0x0004feb8); // 9.99
ASSERT_STREQ_LEN(written, buff, " 10.0");

written = LIBC_NAMESPACE::sprintf(buff, "%-10.2k", 0x0004feb8); // 9.99
ASSERT_STREQ_LEN(written, buff, "9.99 ");

written = LIBC_NAMESPACE::sprintf(buff, "%-5.1k", 0x0004feb8); // 9.99
ASSERT_STREQ_LEN(written, buff, "10.0 ");

written = LIBC_NAMESPACE::sprintf(buff, "%-5.1k", 0x00000001); // accum min
ASSERT_STREQ_LEN(written, buff, "0.0 ");

written = LIBC_NAMESPACE::sprintf(buff, "%30k", 0x7fffffff); // accum max
ASSERT_STREQ_LEN(written, buff, " 65535.999969");

written = LIBC_NAMESPACE::sprintf(buff, "%-30k", 0x7fffffff); // accum max
ASSERT_STREQ_LEN(written, buff, "65535.999969 ");

written = LIBC_NAMESPACE::sprintf(buff, "%20.2lK",
0x3b9ac9ffFD70A3D7); // 999999999.99
ASSERT_STREQ_LEN(written, buff, " 999999999.99");

written = LIBC_NAMESPACE::sprintf(buff, "%20.1lK",
0x3b9ac9ffFD70A3D7); // 999999999.99
ASSERT_STREQ_LEN(written, buff, " 1000000000.0");

written = LIBC_NAMESPACE::sprintf(buff, "%12.3R %-12.3k", 0x1999,
0x00800000); // 0.1, 256.0
ASSERT_STREQ_LEN(written, buff, " 0.100 256.000 ");

written =
LIBC_NAMESPACE::sprintf(buff, "%+-#12.3lk % 012.3k", 0x000000001013a92a,
0x02740000); // 0.126, 1256.0
ASSERT_STREQ_LEN(written, buff, "+0.126 0001256.000");
}
#endif // defined(LIBC_COMPILER_HAS_FIXED_POINT) &&
// !defined(LIBC_COPT_PRINTF_DISABLE_FIXED_POINT)

#ifndef LIBC_COPT_PRINTF_DISABLE_WRITE_INT
TEST(LlvmLibcSPrintfTest, WriteIntConv) {
char buff[64];
Expand Down
14 changes: 8 additions & 6 deletions utils/bazel/llvm-project-overlay/libc/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -2982,20 +2982,21 @@ libc_function(
################################ stdio targets #################################

libc_support_library(
name = "printf_core_structs",
hdrs = ["src/stdio/printf_core/core_structs.h"],
name = "printf_config",
hdrs = ["src/stdio/printf_core/printf_config.h"],
defines = PRINTF_COPTS,
deps = [
":__support_cpp_string_view",
":__support_fputil_fp_bits",
],
)

libc_support_library(
name = "printf_config",
hdrs = ["src/stdio/printf_core/printf_config.h"],
name = "printf_core_structs",
hdrs = ["src/stdio/printf_core/core_structs.h"],
defines = PRINTF_COPTS,
deps = [
":__support_cpp_string_view",
":__support_fputil_fp_bits",
":printf_config",
],
)

Expand Down Expand Up @@ -3081,6 +3082,7 @@ libc_support_library(
":__support_libc_assert",
":__support_uint",
":__support_uint128",
":printf_config",
":printf_core_structs",
":printf_writer",
],
Expand Down