[libc-commits] [libc] f9f8693 - [libc] add printf hexadecimal float conversion

Michael Jones via libc-commits libc-commits at lists.llvm.org
Fri Jul 8 15:58:24 PDT 2022


Author: Michael Jones
Date: 2022-07-08T15:58:20-07:00
New Revision: f9f8693be31c0489647c6425dd6f3feaecf72ef9

URL: https://github.com/llvm/llvm-project/commit/f9f8693be31c0489647c6425dd6f3feaecf72ef9
DIFF: https://github.com/llvm/llvm-project/commit/f9f8693be31c0489647c6425dd6f3feaecf72ef9.diff

LOG: [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

Added: 
    libc/src/stdio/printf_core/float_hex_converter.h

Modified: 
    libc/src/stdio/printf_core/CMakeLists.txt
    libc/src/stdio/printf_core/converter.cpp
    libc/src/stdio/printf_core/converter_atlas.h
    libc/src/stdio/printf_core/parser.cpp
    libc/test/src/stdio/sprintf_test.cpp

Removed: 
    


################################################################################
diff  --git a/libc/src/stdio/printf_core/CMakeLists.txt b/libc/src/stdio/printf_core/CMakeLists.txt
index b30a69f7a9573..565b968657028 100644
--- a/libc/src/stdio/printf_core/CMakeLists.txt
+++ b/libc/src/stdio/printf_core/CMakeLists.txt
@@ -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
 )
 
 

diff  --git a/libc/src/stdio/printf_core/converter.cpp b/libc/src/stdio/printf_core/converter.cpp
index 074b34568b12f..5845229bfff82 100644
--- a/libc/src/stdio/printf_core/converter.cpp
+++ b/libc/src/stdio/printf_core/converter.cpp
@@ -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);

diff  --git a/libc/src/stdio/printf_core/converter_atlas.h b/libc/src/stdio/printf_core/converter_atlas.h
index 71354cc0b96ce..6f32eea135a23 100644
--- a/libc/src/stdio/printf_core/converter_atlas.h
+++ b/libc/src/stdio/printf_core/converter_atlas.h
@@ -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"

diff  --git a/libc/src/stdio/printf_core/float_hex_converter.h b/libc/src/stdio/printf_core/float_hex_converter.h
new file mode 100644
index 0000000000000..b4840b2faa9e0
--- /dev/null
+++ b/libc/src/stdio/printf_core/float_hex_converter.h
@@ -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

diff  --git a/libc/src/stdio/printf_core/parser.cpp b/libc/src/stdio/printf_core/parser.cpp
index 565e6a61b05ae..ddf3cec6f52fa 100644
--- a/libc/src/stdio/printf_core/parser.cpp
+++ b/libc/src/stdio/printf_core/parser.cpp
@@ -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 *>();

diff  --git a/libc/test/src/stdio/sprintf_test.cpp b/libc/test/src/stdio/sprintf_test.cpp
index c274773c9c0ac..765487d90c452 100644
--- a/libc/test/src/stdio/sprintf_test.cpp
+++ b/libc/test/src/stdio/sprintf_test.cpp
@@ -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];


        


More information about the libc-commits mailing list