[libc] add printf hexadecimal float conversion

This patch adds the %a/A conversions to printf, as well as the compiler
flag to disable floating point handling entirely. This will allow our
printf implementation to display every type of argument allowed by
printf, although some formats are still incomplete.

Reviewed By: sivachandra

Differential Revision: https://reviews.llvm.org/D129240
This commit is contained in:
Michael Jones 2022-07-06 16:53:34 -07:00
parent eba6d92f69
commit f9f8693be3
6 changed files with 547 additions and 14 deletions

View File

@ -65,9 +65,11 @@ add_object_library(
ptr_converter.h
oct_converter.h
write_int_converter.h
float_hex_converter.h
DEPENDS
.writer
.core_structs
libc.src.__support.CPP.limits
)

View File

@ -44,19 +44,20 @@ int convert(Writer *writer, const FormatSection &to_conv) {
case 'x':
case 'X':
return convert_hex(writer, to_conv);
// TODO(michaelrj): add a flag to disable float point values here
case 'f':
case 'F':
// return convert_float_decimal(writer, to_conv);
case 'e':
case 'E':
// return convert_float_dec_exp(writer, to_conv);
#ifndef LLVM_LIBC_PRINTF_DISABLE_FLOAT
// case 'f':
// case 'F':
// return convert_float_decimal(writer, to_conv);
// case 'e':
// case 'E':
// return convert_float_dec_exp(writer, to_conv);
case 'a':
case 'A':
// return convert_float_hex_exp(writer, to_conv);
case 'g':
case 'G':
return convert_float_hex_exp(writer, to_conv);
// case 'g':
// case 'G':
// return convert_float_mixed(writer, to_conv);
#endif // LLVM_LIBC_PRINTF_DISABLE_FLOAT
#ifndef LLVM_LIBC_PRINTF_DISABLE_WRITE_INT
case 'n':
return convert_write_int(writer, to_conv);

View File

@ -27,11 +27,13 @@
// defines convert_hex
#include "src/stdio/printf_core/hex_converter.h"
// TODO(michaelrj): add a flag to disable float point values here
#ifndef LLVM_LIBC_PRINTF_DISABLE_FLOAT
// defines convert_float_decimal
// defines convert_float_dec_exp
// defines convert_float_hex_exp
#include "src/stdio/printf_core/float_hex_converter.h"
// defines convert_float_mixed
#endif // LLVM_LIBC_PRINTF_DISABLE_FLOAT
#ifndef LLVM_LIBC_PRINTF_DISABLE_WRITE_INT
#include "src/stdio/printf_core/write_int_converter.h"

View File

@ -0,0 +1,273 @@
//===-- Hexadecimal 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_HEX_CONVERTER_H
#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_FLOAT_HEX_CONVERTER_H
#include "src/__support/FPUtil/FPBits.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 __llvm_libc {
namespace printf_core {
using MantissaInt = fputil::FPBits<long double>::UIntType;
int inline convert_float_hex_exp(Writer *writer, const FormatSection &to_conv) {
// All of the letters will be defined relative to variable a, which will be
// the appropriate case based on the name of the conversion.
// Since the name of the conversion is also 'a', we can just use it directly.
const char a = to_conv.conv_name;
bool is_negative;
int exponent;
MantissaInt mantissa;
bool is_inf_or_nan;
uint32_t mantissa_width;
int exponent_bias;
if (to_conv.length_modifier == LengthModifier::L) {
mantissa_width = fputil::MantissaWidth<long double>::VALUE;
exponent_bias = fputil::FPBits<long double>::EXPONENT_BIAS;
fputil::FPBits<long double>::UIntType float_raw = to_conv.conv_val_raw;
fputil::FPBits<long double> float_bits(float_raw);
is_negative = float_bits.get_sign();
exponent = float_bits.get_exponent();
mantissa = float_bits.get_explicit_mantissa();
is_inf_or_nan = float_bits.is_inf_or_nan();
} else {
mantissa_width = fputil::MantissaWidth<double>::VALUE;
exponent_bias = fputil::FPBits<double>::EXPONENT_BIAS;
fputil::FPBits<double>::UIntType float_raw = to_conv.conv_val_raw;
fputil::FPBits<double> float_bits(float_raw);
is_negative = float_bits.get_sign();
exponent = float_bits.get_exponent();
mantissa = float_bits.get_explicit_mantissa();
is_inf_or_nan = float_bits.is_inf_or_nan();
}
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 = ' ';
// TODO: move the inf/nan handling to a seperate conversion, since the
// functionality is identical accross all float conversions.
if (is_inf_or_nan) {
// Both "inf" and "nan" are the same number of characters, being 3.
int padding = to_conv.min_width - (sign_char > 0 ? 1 : 0) - 3;
// The right justified pattern is (spaces), (sign), inf/nan
// The left justified pattern is (sign), inf/nan, (spaces)
if (padding > 0 && ((to_conv.flags & FormatFlags::LEFT_JUSTIFIED) !=
FormatFlags::LEFT_JUSTIFIED))
RET_IF_RESULT_NEGATIVE(writer->write_chars(' ', padding));
if (sign_char)
RET_IF_RESULT_NEGATIVE(writer->write(&sign_char, 1));
if (mantissa == 0) { // inf
RET_IF_RESULT_NEGATIVE(writer->write((a == 'a' ? "inf" : "INF"), 3));
} else { // nan
RET_IF_RESULT_NEGATIVE(writer->write((a == 'a' ? "nan" : "NAN"), 3));
}
if (padding > 0 && ((to_conv.flags & FormatFlags::LEFT_JUSTIFIED) ==
FormatFlags::LEFT_JUSTIFIED))
RET_IF_RESULT_NEGATIVE(writer->write_chars(' ', padding));
return 0;
}
// Handle the exponent for numbers with a 0 exponent
if (exponent == -exponent_bias) {
if (mantissa > 0) // Subnormals
++exponent;
else // Zeroes
exponent = 0;
}
constexpr size_t BITS_IN_HEX_DIGIT = 4;
// This is to handle situations where the mantissa isn't an even number of hex
// digits. This is primarily relevant for x86 80 bit long doubles, which have
// 63 bit mantissas.
if (mantissa_width % BITS_IN_HEX_DIGIT != 0) {
exponent -= mantissa_width % BITS_IN_HEX_DIGIT;
}
// This is the max number of digits it can take to represent the mantissa.
// Since the number is in bits, we divide by 4, and then add one to account
// for the extra implicit bit. We use the larger of the two possible values
// since the size must be constant.
constexpr size_t MANT_BUFF_LEN =
(fputil::MantissaWidth<long double>::VALUE / BITS_IN_HEX_DIGIT) + 1;
char mant_buffer[MANT_BUFF_LEN];
size_t mant_len = (mantissa_width / BITS_IN_HEX_DIGIT) + 1;
// Precision only tracks the number of digits after the hexadecimal point, so
// we have to add one to account for the digit before the hexadecimal point.
if (to_conv.precision + 1 < static_cast<int>(mant_len) &&
to_conv.precision + 1 > 0) {
const size_t intended_digits = to_conv.precision + 1;
const size_t shift_amount =
(mant_len - intended_digits) * BITS_IN_HEX_DIGIT;
const MantissaInt truncated_bits =
mantissa & ((MantissaInt(1) << shift_amount) - 1);
const MantissaInt halfway_const = MantissaInt(1) << (shift_amount - 1);
mantissa >>= shift_amount;
// Round to nearest, if it's exactly halfway then round to even.
if (truncated_bits > halfway_const)
++mantissa;
else if (truncated_bits == halfway_const)
mantissa = mantissa + (mantissa & 1);
// If the rounding caused an overflow, shift the mantissa and adjust the
// exponent to match.
if (mantissa >= (MantissaInt(1) << (intended_digits * BITS_IN_HEX_DIGIT))) {
mantissa >>= BITS_IN_HEX_DIGIT;
exponent += BITS_IN_HEX_DIGIT;
}
mant_len = intended_digits;
}
size_t mant_cur = mant_len;
size_t first_non_zero = 1;
for (; mant_cur > 0; --mant_cur, mantissa /= 16) {
char new_digit = ((mantissa % 16) > 9) ? ((mantissa % 16) - 10 + a)
: ((mantissa % 16) + '0');
mant_buffer[mant_cur - 1] = new_digit;
if (new_digit != '0' && first_non_zero < mant_cur)
first_non_zero = mant_cur;
}
size_t mant_digits = first_non_zero;
if (to_conv.precision >= 0)
mant_digits = mant_len;
// This approximates the number of digits it will take to represent the
// exponent. The calculation is ceil((bits * 5) / 16). Floor also works, but
// only on exact multiples of 16. We add 1 for the sign.
// Relevant sizes:
// 15 -> 5
// 11 -> 4
// 8 -> 3
constexpr size_t EXP_LEN =
(((fputil::ExponentWidth<long double>::VALUE * 5) + 15) / 16) + 1;
char exp_buffer[EXP_LEN];
bool exp_is_negative = false;
if (exponent < 0) {
exp_is_negative = true;
exponent = -exponent;
}
size_t exp_cur = EXP_LEN;
for (; exponent > 0; --exp_cur, exponent /= 10) {
exp_buffer[exp_cur - 1] = (exponent % 10) + '0';
}
if (exp_cur == EXP_LEN) { // if nothing else was written, write a 0.
exp_buffer[EXP_LEN - 1] = '0';
exp_cur = EXP_LEN - 1;
}
exp_buffer[exp_cur - 1] = exp_is_negative ? '-' : '+';
--exp_cur;
// these are signed to prevent underflow due to negative values. The eventual
// values will always be non-negative.
int trailing_zeroes = 0;
int padding;
// prefix is "0x", and always appears.
constexpr size_t PREFIX_LEN = 2;
char prefix[PREFIX_LEN];
prefix[0] = '0';
prefix[1] = a + ('x' - 'a');
// If the precision is greater than the actual result, pad with 0s
if (to_conv.precision > static_cast<int>(mant_digits - 1))
trailing_zeroes = to_conv.precision - (mant_digits - 1);
bool has_hexadecimal_point =
(mant_digits > 1) || ((to_conv.flags & FormatFlags::ALTERNATE_FORM) ==
FormatFlags::ALTERNATE_FORM);
constexpr char HEXADECIMAL_POINT = '.';
// This is for the letter 'p' before the exponent.
const char exp_seperator = a + ('p' - 'a');
constexpr int EXP_SEPERATOR_LEN = 1;
padding = to_conv.min_width - (sign_char > 0 ? 1 : 0) - PREFIX_LEN -
mant_digits - (has_hexadecimal_point ? 1 : 0) - EXP_SEPERATOR_LEN -
(EXP_LEN - exp_cur);
if (padding < 0)
padding = 0;
if ((to_conv.flags & FormatFlags::LEFT_JUSTIFIED) ==
FormatFlags::LEFT_JUSTIFIED) {
// The pattern is (sign), 0x, digit, (.), (other digits), (zeroes), p,
// exponent, (spaces)
if (sign_char > 0)
RET_IF_RESULT_NEGATIVE(writer->write(&sign_char, 1));
RET_IF_RESULT_NEGATIVE(writer->write(prefix, PREFIX_LEN));
RET_IF_RESULT_NEGATIVE(writer->write(mant_buffer, 1));
if (has_hexadecimal_point)
RET_IF_RESULT_NEGATIVE(writer->write(&HEXADECIMAL_POINT, 1));
if (mant_digits > 1)
RET_IF_RESULT_NEGATIVE(writer->write(mant_buffer + 1, mant_digits - 1));
if (trailing_zeroes > 0)
RET_IF_RESULT_NEGATIVE(writer->write_chars('0', trailing_zeroes));
RET_IF_RESULT_NEGATIVE(writer->write(&exp_seperator, EXP_SEPERATOR_LEN));
RET_IF_RESULT_NEGATIVE(
writer->write(exp_buffer + exp_cur, EXP_LEN - exp_cur));
if (padding > 0)
RET_IF_RESULT_NEGATIVE(writer->write_chars(' ', padding));
} else {
// The pattern is (spaces), (sign), 0x, (zeroes), digit, (.), (other
// digits), (zeroes), p, exponent
if ((padding > 0) && ((to_conv.flags & FormatFlags::LEADING_ZEROES) !=
FormatFlags::LEADING_ZEROES))
RET_IF_RESULT_NEGATIVE(writer->write_chars(' ', padding));
if (sign_char > 0)
RET_IF_RESULT_NEGATIVE(writer->write(&sign_char, 1));
RET_IF_RESULT_NEGATIVE(writer->write(prefix, PREFIX_LEN));
if ((padding > 0) && ((to_conv.flags & FormatFlags::LEADING_ZEROES) ==
FormatFlags::LEADING_ZEROES))
RET_IF_RESULT_NEGATIVE(writer->write_chars('0', padding));
RET_IF_RESULT_NEGATIVE(writer->write(mant_buffer, 1));
if (has_hexadecimal_point)
RET_IF_RESULT_NEGATIVE(writer->write(&HEXADECIMAL_POINT, 1));
if (mant_digits > 1)
RET_IF_RESULT_NEGATIVE(writer->write(mant_buffer + 1, mant_digits - 1));
if (trailing_zeroes > 0)
RET_IF_RESULT_NEGATIVE(writer->write_chars('0', trailing_zeroes));
RET_IF_RESULT_NEGATIVE(writer->write(&exp_seperator, EXP_SEPERATOR_LEN));
RET_IF_RESULT_NEGATIVE(
writer->write(exp_buffer + exp_cur, EXP_LEN - exp_cur));
}
return 0;
}
} // namespace printf_core
} // namespace __llvm_libc
#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_FLOAT_HEX_CONVERTER_H

View File

@ -121,7 +121,7 @@ FormatSection Parser::get_next_section() {
break;
}
break;
// TODO(michaelrj): add a flag to disable float point values here
#ifndef LLVM_LIBC_PRINTF_DISABLE_FLOAT
case ('f'):
case ('F'):
case ('e'):
@ -137,6 +137,7 @@ FormatSection Parser::get_next_section() {
section.conv_val_raw = bit_cast<fputil::FPBits<long double>::UIntType>(
GET_ARG_VAL_SIMPLEST(long double, conv_index));
break;
#endif // LLVM_LIBC_PRINTF_DISABLE_FLOAT
#ifndef LLVM_LIBC_PRINTF_DISABLE_WRITE_INT
case ('n'):
#endif // LLVM_LIBC_PRINTF_DISABLE_WRITE_INT
@ -339,7 +340,7 @@ Parser::TypeDesc Parser::get_type_desc(size_t index) {
break;
}
break;
// TODO(michaelrj): add a flag to disable float point values here
#ifndef LLVM_LIBC_PRINTF_DISABLE_FLOAT
case ('f'):
case ('F'):
case ('e'):
@ -353,6 +354,7 @@ Parser::TypeDesc Parser::get_type_desc(size_t index) {
else
conv_size = TYPE_DESC<long double>;
break;
#endif // LLVM_LIBC_PRINTF_DISABLE_FLOAT
#ifndef LLVM_LIBC_PRINTF_DISABLE_WRITE_INT
case ('n'):
#endif // LLVM_LIBC_PRINTF_DISABLE_WRITE_INT
@ -395,12 +397,13 @@ void Parser::args_to_index(size_t index) {
args_cur.next_var<uint32_t>();
else if (cur_type_desc == TYPE_DESC<uint64_t>)
args_cur.next_var<uint64_t>();
// TODO(michaelrj): add a flag to disable float point values here
#ifndef LLVM_LIBC_PRINTF_DISABLE_FLOAT
// Floating point numbers are stored separately from the other arguments.
else if (cur_type_desc == TYPE_DESC<double>)
args_cur.next_var<double>();
else if (cur_type_desc == TYPE_DESC<long double>)
args_cur.next_var<long double>();
#endif // LLVM_LIBC_PRINTF_DISABLE_FLOAT
// pointers may be stored separately from normal values.
else if (cur_type_desc == TYPE_DESC<void *>)
args_cur.next_var<void *>();

View File

@ -8,8 +8,15 @@
#include "src/stdio/sprintf.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/PlatformDefs.h"
#include "utils/UnitTest/Test.h"
// Subtract 1 from sizeof(expected_str) to account for the null byte.
#define ASSERT_STREQ_LEN(actual_written, actual_str, expected_str) \
EXPECT_EQ(actual_written, static_cast<int>(sizeof(expected_str) - 1)); \
EXPECT_STREQ(actual_str, expected_str);
TEST(LlvmLibcSPrintfTest, SimpleNoConv) {
char buff[64];
int written;
@ -483,6 +490,251 @@ TEST(LlvmLibcSPrintfTest, OctConv) {
ASSERT_STREQ(buff, "0077 01000000000000 002 ");
}
#ifndef LLVM_LIBC_PRINTF_DISABLE_FLOAT
TEST(LlvmLibcSPrintfTest, FloatHexExpConv) {
char buff[64];
int written;
double inf = __llvm_libc::fputil::FPBits<double>::inf().get_val();
double nan = __llvm_libc::fputil::FPBits<double>::build_nan(1);
written = __llvm_libc::sprintf(buff, "%a", 1.0);
ASSERT_STREQ_LEN(written, buff, "0x1p+0");
written = __llvm_libc::sprintf(buff, "%A", -1.0);
ASSERT_STREQ_LEN(written, buff, "-0X1P+0");
written = __llvm_libc::sprintf(buff, "%a", -0x1.abcdef12345p0);
ASSERT_STREQ_LEN(written, buff, "-0x1.abcdef12345p+0");
written = __llvm_libc::sprintf(buff, "%A", 0x1.abcdef12345p0);
ASSERT_STREQ_LEN(written, buff, "0X1.ABCDEF12345P+0");
written = __llvm_libc::sprintf(buff, "%a", 0.0);
ASSERT_STREQ_LEN(written, buff, "0x0p+0");
written = __llvm_libc::sprintf(buff, "%a", 1.0e100);
ASSERT_STREQ_LEN(written, buff, "0x1.249ad2594c37dp+332");
written = __llvm_libc::sprintf(buff, "%a", 0.1);
ASSERT_STREQ_LEN(written, buff, "0x1.999999999999ap-4");
// Subnormal Tests.
written = __llvm_libc::sprintf(buff, "%a", 0x1.0p-1027);
ASSERT_STREQ_LEN(written, buff, "0x0.08p-1022");
written = __llvm_libc::sprintf(buff, "%a", 0x1.0p-1025);
ASSERT_STREQ_LEN(written, buff, "0x0.2p-1022");
written = __llvm_libc::sprintf(buff, "%a", 0x1.0p-1023);
ASSERT_STREQ_LEN(written, buff, "0x0.8p-1022");
written = __llvm_libc::sprintf(buff, "%a", 0x1.0p-1022);
ASSERT_STREQ_LEN(written, buff, "0x1p-1022");
written = __llvm_libc::sprintf(buff, "%a", 0x1.0p-1074);
ASSERT_STREQ_LEN(written, buff, "0x0.0000000000001p-1022");
// Inf/Nan Tests.
written = __llvm_libc::sprintf(buff, "%a", inf);
ASSERT_STREQ_LEN(written, buff, "inf");
written = __llvm_libc::sprintf(buff, "%A", -inf);
ASSERT_STREQ_LEN(written, buff, "-INF");
written = __llvm_libc::sprintf(buff, "%a", nan);
ASSERT_STREQ_LEN(written, buff, "nan");
written = __llvm_libc::sprintf(buff, "%A", -nan);
ASSERT_STREQ_LEN(written, buff, "-NAN");
// Length Modifier Tests.
written = __llvm_libc::sprintf(buff, "%La", 0.1L);
#if defined(SPECIAL_X86_LONG_DOUBLE)
ASSERT_STREQ_LEN(written, buff, "0xc.ccccccccccccccdp-7");
#elif defined(LONG_DOUBLE_IS_DOUBLE)
ASSERT_STREQ_LEN(written, buff, "0x1.999999999999ap-4");
#else // 128 bit long double
ASSERT_STREQ_LEN(written, buff, "0x1.999999999999999999999999999ap-4");
#endif
written = __llvm_libc::sprintf(buff, "%La", 1.0e1000L);
#if defined(SPECIAL_X86_LONG_DOUBLE)
ASSERT_STREQ_LEN(written, buff, "0xf.38db1f9dd3dac05p+3318");
#elif defined(LONG_DOUBLE_IS_DOUBLE)
ASSERT_STREQ_LEN(written, buff, "inf");
#else // 128 bit long double
ASSERT_STREQ_LEN(written, buff, "0x1.e71b63f3ba7b580af1a52d2a7379p+3321");
#endif
written = __llvm_libc::sprintf(buff, "%La", 1.0e-1000L);
#if defined(SPECIAL_X86_LONG_DOUBLE)
ASSERT_STREQ_LEN(written, buff, "0x8.68a9188a89e1467p-3325");
#elif defined(LONG_DOUBLE_IS_DOUBLE)
ASSERT_STREQ_LEN(written, buff, "0x0p+0");
#else // 128 bit long double
ASSERT_STREQ_LEN(written, buff, "0x1.0d152311513c28ce202627c06ec2p-3322");
#endif
// Min Width Tests.
written = __llvm_libc::sprintf(buff, "%15a", 1.0);
ASSERT_STREQ_LEN(written, buff, " 0x1p+0");
written = __llvm_libc::sprintf(buff, "%15a", -1.0);
ASSERT_STREQ_LEN(written, buff, " -0x1p+0");
written = __llvm_libc::sprintf(buff, "%15a", 1.0e10);
ASSERT_STREQ_LEN(written, buff, " 0x1.2a05f2p+33");
written = __llvm_libc::sprintf(buff, "%15a", -1.0e10);
ASSERT_STREQ_LEN(written, buff, "-0x1.2a05f2p+33");
written = __llvm_libc::sprintf(buff, "%10a", 1.0e10);
ASSERT_STREQ_LEN(written, buff, "0x1.2a05f2p+33");
written = __llvm_libc::sprintf(buff, "%5a", inf);
ASSERT_STREQ_LEN(written, buff, " inf");
written = __llvm_libc::sprintf(buff, "%5a", -nan);
ASSERT_STREQ_LEN(written, buff, " -nan");
// Precision Tests.
written = __llvm_libc::sprintf(buff, "%.1a", 1.0);
ASSERT_STREQ_LEN(written, buff, "0x1.0p+0");
written = __llvm_libc::sprintf(buff, "%.1a", 0.0);
ASSERT_STREQ_LEN(written, buff, "0x0.0p+0");
written = __llvm_libc::sprintf(buff, "%.1a", 0.1);
ASSERT_STREQ_LEN(written, buff, "0x1.ap-4");
written = __llvm_libc::sprintf(buff, "%.1a", 0x1.0fp0);
ASSERT_STREQ_LEN(written, buff, "0x1.1p+0");
written = __llvm_libc::sprintf(buff, "%.1a", 0x1.07p0);
ASSERT_STREQ_LEN(written, buff, "0x1.0p+0");
written = __llvm_libc::sprintf(buff, "%.1a", 0x1.08p0);
ASSERT_STREQ_LEN(written, buff, "0x1.0p+0");
written = __llvm_libc::sprintf(buff, "%.1a", 0x1.18p0);
ASSERT_STREQ_LEN(written, buff, "0x1.2p+0");
written = __llvm_libc::sprintf(buff, "%.1a", 0x1.ffp0);
ASSERT_STREQ_LEN(written, buff, "0x2.0p+0");
written = __llvm_libc::sprintf(buff, "%.5a", 1.25);
ASSERT_STREQ_LEN(written, buff, "0x1.40000p+0");
written = __llvm_libc::sprintf(buff, "%.0a", 1.25);
ASSERT_STREQ_LEN(written, buff, "0x1p+0");
written = __llvm_libc::sprintf(buff, "%.0a", 1.75);
ASSERT_STREQ_LEN(written, buff, "0x2p+0");
written = __llvm_libc::sprintf(buff, "%.1a", 0x1.0p-1023);
ASSERT_STREQ_LEN(written, buff, "0x0.8p-1022");
written = __llvm_libc::sprintf(buff, "%.1a", 0x1.8p-1023);
ASSERT_STREQ_LEN(written, buff, "0x0.cp-1022");
written = __llvm_libc::sprintf(buff, "%.1a", 0x1.0p-1024);
ASSERT_STREQ_LEN(written, buff, "0x0.4p-1022");
written = __llvm_libc::sprintf(buff, "%.0a", 0x1.0p-1023);
ASSERT_STREQ_LEN(written, buff, "0x0p-1022");
written = __llvm_libc::sprintf(buff, "%.0a", 0x1.8p-1023);
ASSERT_STREQ_LEN(written, buff, "0x1p-1022");
written = __llvm_libc::sprintf(buff, "%.0a", 0x1.0p-1024);
ASSERT_STREQ_LEN(written, buff, "0x0p-1022");
written = __llvm_libc::sprintf(buff, "%.2a", 0x1.0p-1027);
ASSERT_STREQ_LEN(written, buff, "0x0.08p-1022");
written = __llvm_libc::sprintf(buff, "%.1a", 0x1.0p-1027);
ASSERT_STREQ_LEN(written, buff, "0x0.0p-1022");
written = __llvm_libc::sprintf(buff, "%.5a", 0.0);
ASSERT_STREQ_LEN(written, buff, "0x0.00000p+0");
written = __llvm_libc::sprintf(buff, "%.5a", 0x1.008p0);
ASSERT_STREQ_LEN(written, buff, "0x1.00800p+0");
written = __llvm_libc::sprintf(buff, "%.5a", 0x1.008p10);
ASSERT_STREQ_LEN(written, buff, "0x1.00800p+10");
written = __llvm_libc::sprintf(buff, "%.5a", nan);
ASSERT_STREQ_LEN(written, buff, "nan");
written = __llvm_libc::sprintf(buff, "%.1La", 0.1L);
#if defined(SPECIAL_X86_LONG_DOUBLE)
ASSERT_STREQ_LEN(written, buff, "0xc.dp-7");
#elif defined(LONG_DOUBLE_IS_DOUBLE)
ASSERT_STREQ_LEN(written, buff, "0x1.ap-4");
#else // 128 bit long double
ASSERT_STREQ_LEN(written, buff, "0x1.ap-4");
#endif
written = __llvm_libc::sprintf(buff, "%.1La", 0xf.fffffffffffffffp16380L);
#if defined(SPECIAL_X86_LONG_DOUBLE)
ASSERT_STREQ_LEN(written, buff, "0x1.0p+16384");
#elif defined(LONG_DOUBLE_IS_DOUBLE)
ASSERT_STREQ_LEN(written, buff, "inf");
#else // 128 bit long double
ASSERT_STREQ_LEN(written, buff, "0x2.0p+16383");
#endif
// Flag Tests.
written = __llvm_libc::sprintf(buff, "%+a", nan);
ASSERT_STREQ_LEN(written, buff, "+nan");
written = __llvm_libc::sprintf(buff, "% A", inf);
ASSERT_STREQ_LEN(written, buff, " INF");
written = __llvm_libc::sprintf(buff, "%-5a", inf);
ASSERT_STREQ_LEN(written, buff, "inf ");
written = __llvm_libc::sprintf(buff, "%+-5A", nan);
ASSERT_STREQ_LEN(written, buff, "+NAN ");
written = __llvm_libc::sprintf(buff, "%+a", 1.0);
ASSERT_STREQ_LEN(written, buff, "+0x1p+0");
written = __llvm_libc::sprintf(buff, "% a", 0.0);
ASSERT_STREQ_LEN(written, buff, " 0x0p+0");
written = __llvm_libc::sprintf(buff, "%-10a", 1.5);
ASSERT_STREQ_LEN(written, buff, "0x1.8p+0 ");
written = __llvm_libc::sprintf(buff, "%#a", 1.0);
ASSERT_STREQ_LEN(written, buff, "0x1.p+0");
written = __llvm_libc::sprintf(buff, "%#.0a", 1.5);
ASSERT_STREQ_LEN(written, buff, "0x2.p+0");
written = __llvm_libc::sprintf(buff, "%010a", 1.5);
ASSERT_STREQ_LEN(written, buff, "0x001.8p+0");
written = __llvm_libc::sprintf(buff, "%+- #0a", 0.0);
ASSERT_STREQ_LEN(written, buff, "+0x0.p+0");
// Combined Tests.
written = __llvm_libc::sprintf(buff, "%12.3a %-12.3A", 0.1, 256.0);
ASSERT_STREQ_LEN(written, buff, " 0x1.99ap-4 0X1.000P+8 ");
written = __llvm_libc::sprintf(buff, "%+-#12.3a % 012.3a", 0.1256, 1256.0);
ASSERT_STREQ_LEN(written, buff, "+0x1.014p-3 0x1.3a0p+10");
}
#endif // LLVM_LIBC_PRINTF_DISABLE_FLOAT
#ifndef LLVM_LIBC_PRINTF_DISABLE_WRITE_INT
TEST(LlvmLibcSPrintfTest, WriteIntConv) {
char buff[64];