[libc-commits] [libc] 173d502 - [libc] add printf auto float conversion

Michael Jones via libc-commits libc-commits at lists.llvm.org
Fri Feb 3 15:17:42 PST 2023


Author: Michael Jones
Date: 2023-02-03T15:17:37-08:00
New Revision: 173d50235fa3c5b028fa3c8efa5beec56b752e75

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

LOG: [libc] add printf auto float conversion

This patch adds the final conversion to printf, %g. This is a floating
point conversion that selects automatically between the %e and %f
formats based on the precision requested and resulting exponent.
Additionally it trims trailing zeroes. With this done all that's left
for finishing printf is adding long double support to the decimal float
conversions.

Reviewed By: sivachandra

Differential Revision: https://reviews.llvm.org/D143006

Added: 
    

Modified: 
    libc/src/__support/float_to_string.h
    libc/src/stdio/printf_core/converter.cpp
    libc/src/stdio/printf_core/converter_atlas.h
    libc/src/stdio/printf_core/float_dec_converter.h
    libc/test/src/stdio/sprintf_test.cpp

Removed: 
    


################################################################################
diff  --git a/libc/src/__support/float_to_string.h b/libc/src/__support/float_to_string.h
index 47b7ae89dcd14..7ea3c5da78211 100644
--- a/libc/src/__support/float_to_string.h
+++ b/libc/src/__support/float_to_string.h
@@ -89,6 +89,8 @@ LIBC_INLINE constexpr uint32_t length_for_num(const uint32_t idx,
 // Rewritten slightly we get:
 // floor(5^(-9i) * 2^(e + c_1 - 9i) + 1) % (10^9 * 2^c_1)
 
+// TODO: Fix long doubles (needs bigger table or alternate algorithm.)
+// Currently the table values are generated, which is very slow.
 template <size_t INT_SIZE>
 LIBC_INLINE constexpr cpp::UInt<MID_INT_SIZE>
 get_table_positive(int exponent, size_t i, const size_t constant) {

diff  --git a/libc/src/stdio/printf_core/converter.cpp b/libc/src/stdio/printf_core/converter.cpp
index 2ceee7a56372c..7060d9c5e2eaa 100644
--- a/libc/src/stdio/printf_core/converter.cpp
+++ b/libc/src/stdio/printf_core/converter.cpp
@@ -52,9 +52,9 @@ int convert(Writer *writer, const FormatSection &to_conv) {
   case 'a':
   case 'A':
     return convert_float_hex_exp(writer, to_conv);
-    // case 'g':
-    // case 'G':
-    // return convert_float_mixed(writer, to_conv);
+  case 'g':
+  case 'G':
+    return convert_float_dec_auto(writer, to_conv);
 #endif // LLVM_LIBC_PRINTF_DISABLE_FLOAT
 #ifndef LLVM_LIBC_PRINTF_DISABLE_WRITE_INT
   case 'n':

diff  --git a/libc/src/stdio/printf_core/converter_atlas.h b/libc/src/stdio/printf_core/converter_atlas.h
index 2ad0c726af010..592474bdfdc0f 100644
--- a/libc/src/stdio/printf_core/converter_atlas.h
+++ b/libc/src/stdio/printf_core/converter_atlas.h
@@ -24,11 +24,11 @@
 
 #ifndef LLVM_LIBC_PRINTF_DISABLE_FLOAT
 // defines convert_float_decimal
-#include "src/stdio/printf_core/float_dec_converter.h"
 // defines convert_float_dec_exp
+// defines convert_float_dec_auto
+#include "src/stdio/printf_core/float_dec_converter.h"
 // 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

diff  --git a/libc/src/stdio/printf_core/float_dec_converter.h b/libc/src/stdio/printf_core/float_dec_converter.h
index 0fc1d1144d706..e4c3e3edd0540 100644
--- a/libc/src/stdio/printf_core/float_dec_converter.h
+++ b/libc/src/stdio/printf_core/float_dec_converter.h
@@ -427,7 +427,8 @@ class FloatWriter {
         // 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;
+        total_digits =
+            (has_decimal_point ? 1 : 0) + 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
@@ -580,38 +581,38 @@ LIBC_INLINE int convert_float_decimal_typed(Writer *writer,
       } else {
 
         const uint32_t maximum = precision - BLOCK_SIZE * i;
-        uint32_t lastDigit = 0;
+        uint32_t last_digit = 0;
         for (uint32_t k = 0; k < BLOCK_SIZE - maximum; ++k) {
-          lastDigit = digits % 10;
+          last_digit = 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;
+            -exponent - MANT_WIDTH - static_cast<int32_t>(precision) - 1;
         const bool trailingZeros =
             requiredTwos <= 0 ||
             (requiredTwos < 60 &&
              multiple_of_power_of_2(float_bits.get_explicit_mantissa(),
-                                    (uint32_t)requiredTwos));
+                                    static_cast<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;
+          if (last_digit != 5) {
+            round = last_digit > 5 ? RoundDirection::Up : RoundDirection::Down;
           } else {
             round = trailingZeros ? RoundDirection::Even : RoundDirection::Up;
           }
           break;
         case FE_DOWNWARD:
-          if (is_negative && (!trailingZeros || lastDigit > 0)) {
+          if (is_negative && (!trailingZeros || last_digit > 0)) {
             round = RoundDirection::Up;
           } else {
             round = RoundDirection::Down;
           }
           break;
         case FE_UPWARD:
-          if (!is_negative && (!trailingZeros || lastDigit > 0)) {
+          if (!is_negative && (!trailingZeros || last_digit > 0)) {
             round = RoundDirection::Up;
           } else {
             round = RoundDirection::Down;
@@ -746,37 +747,46 @@ LIBC_INLINE int convert_float_dec_exp_typed(Writer *writer,
 
   // This is the last block.
   const uint32_t maximum = precision + 1 - digits_written;
-  uint32_t lastDigit = 0;
+  uint32_t last_digit = 0;
   for (uint32_t k = 0; k < last_block_size - maximum; ++k) {
-    lastDigit = digits % 10;
+    last_digit = digits % 10;
     digits /= 10;
   }
+
+  // If the last block we read doesn't have the digit after the end of what
+  // we'll print, then we need to read the next block to get that digit.
+  if (maximum == last_block_size) {
+    BlockInt extra_block = float_converter.get_block(cur_block - 1);
+    last_digit = extra_block / ((MAX_BLOCK / 10) + 1);
+  }
+
   RoundDirection round;
   // Is m * 10^(additionalDigits + 1) / 2^(-exponent) integer?
-  const int32_t requiredTwos = -exponent - MANT_WIDTH - (int32_t)precision - 1;
+  const int32_t requiredTwos =
+      -exponent - MANT_WIDTH - static_cast<int32_t>(precision) - 1;
   const bool trailingZeros =
       requiredTwos <= 0 ||
       (requiredTwos < 60 &&
        multiple_of_power_of_2(float_bits.get_explicit_mantissa(),
-                              (uint32_t)requiredTwos));
+                              static_cast<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;
+    if (last_digit != 5) {
+      round = last_digit > 5 ? RoundDirection::Up : RoundDirection::Down;
     } else {
       round = trailingZeros ? RoundDirection::Even : RoundDirection::Up;
     }
     break;
   case FE_DOWNWARD:
-    if (is_negative && (!trailingZeros || lastDigit > 0)) {
+    if (is_negative && (!trailingZeros || last_digit > 0)) {
       round = RoundDirection::Up;
     } else {
       round = RoundDirection::Down;
     }
     break;
   case FE_UPWARD:
-    if (!is_negative && (!trailingZeros || lastDigit > 0)) {
+    if (!is_negative && (!trailingZeros || last_digit > 0)) {
       round = RoundDirection::Up;
     } else {
       round = RoundDirection::Down;
@@ -794,6 +804,334 @@ LIBC_INLINE int convert_float_dec_exp_typed(Writer *writer,
   return WRITE_OK;
 }
 
+template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
+LIBC_INLINE int convert_float_dec_auto_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();
+
+  // From the standard: Let P (init_precision) equal the precision if nonzero, 6
+  // if the precision is omitted, or 1 if the precision is zero.
+  const size_t init_precision = to_conv.precision <= 0
+                                    ? (to_conv.precision == 0 ? 1 : 6)
+                                    : to_conv.precision;
+
+  //  Then, if a conversion with style E would have an exponent of X
+  //  (base_10_exp):
+  int base_10_exp = 0;
+  // If P > X >= -4 the conversion is with style F and precision P - (X + 1).
+  // Otherwise, the conversion is with style E and precision P - 1.
+
+  // For calculating the base 10 exponent, we need to process the number as if
+  // it has style E, so here we calculate the precision we'll use in that case.
+  const size_t exp_precision = init_precision - 1;
+
+  FloatToString<T> float_converter(static_cast<T>(float_bits));
+
+  // 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.
+  if (mantissa != 0) {
+    // This loop finds the first non-zero block.
+    while (digits == 0) {
+      --cur_block;
+      digits = float_converter.get_block(cur_block);
+    }
+  } else {
+    // In the case of 0.0, then it's always decimal format. If we don't have alt
+    // form then the trailing zeroes are trimmed to make "0", else the precision
+    // is 1 less than specified by the user.
+    FormatSection new_conv = to_conv;
+    if ((to_conv.flags & FormatFlags::ALTERNATE_FORM) != 0) {
+      // This is a style F conversion, making the precision P - 1 - X, but since
+      // this is for the number 0, X (the base 10 exponent) is always 0.
+      new_conv.precision = init_precision - 1;
+    } else {
+      new_conv.precision = 0;
+    }
+    return convert_float_decimal_typed<T>(writer, new_conv, float_bits);
+  }
+
+  size_t block_width = 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);
+    block_width = int_to_str.size();
+  }
+
+  size_t digits_checked = 0;
+  // TODO: look into unifying trailing_zeroes and trailing_nines. The number can
+  // end in a nine or a zero, but not both.
+  size_t trailing_zeroes = 0;
+  size_t trailing_nines = 0;
+
+  base_10_exp = (cur_block * BLOCK_SIZE) + (block_width - 1);
+
+  // If the first block is not also the last block
+  if (block_width <= exp_precision + 1) {
+    char buf[IntegerToString::dec_bufsize<intmax_t>()];
+    auto int_to_str = *IntegerToString::dec(digits, buf);
+
+    for (size_t i = 0; i < block_width; ++i) {
+      if (int_to_str[i] == '9') {
+        ++trailing_nines;
+        trailing_zeroes = 0;
+      } else if (int_to_str[i] == '0') {
+        ++trailing_zeroes;
+        trailing_nines = 0;
+      } else {
+        trailing_nines = 0;
+        trailing_zeroes = 0;
+      }
+    }
+    digits_checked += block_width;
+    --cur_block;
+  }
+
+  // Handle middle blocks
+  for (; digits_checked + BLOCK_SIZE < exp_precision + 1; --cur_block) {
+    digits = float_converter.get_block(cur_block);
+    digits_checked += BLOCK_SIZE;
+    if (digits == MAX_BLOCK) {
+      trailing_nines += 9;
+      trailing_zeroes = 0;
+    } else if (digits == 0) {
+      trailing_zeroes += 9;
+      trailing_nines = 0;
+    } else {
+      // The block is neither all nines nor all zeroes, so we need to figure out
+      // what it ends with.
+      trailing_nines = 0;
+      trailing_zeroes = 0;
+      BlockInt copy_of_digits = digits;
+      int cur_last_digit = copy_of_digits % 10;
+      // We only care if it ends in nines or zeroes.
+      while (copy_of_digits > 0 &&
+             (cur_last_digit == 9 || cur_last_digit == 0)) {
+        // If the next digit is not the same as the previous one, then there are
+        // no more contiguous trailing digits.
+        if ((copy_of_digits % 10) != cur_last_digit) {
+          break;
+        }
+        if (cur_last_digit == 9) {
+          ++trailing_nines;
+        } else if (cur_last_digit == 0) {
+          ++trailing_zeroes;
+        } else {
+          break;
+        }
+        copy_of_digits /= 10;
+      }
+    }
+  }
+
+  // Handle the last block
+
+  digits = float_converter.get_block(cur_block);
+
+  size_t last_block_size = BLOCK_SIZE;
+
+  char buf[IntegerToString::dec_bufsize<intmax_t>()];
+  auto int_to_str = *IntegerToString::dec(digits, buf);
+
+  int implicit_leading_zeroes = BLOCK_SIZE - int_to_str.size();
+
+  // if the last block is also the first block, then ignore leading zeroes.
+  if (digits_checked == 0) {
+    last_block_size = int_to_str.size();
+    implicit_leading_zeroes = 0;
+  } else {
+    // If the block is not the maximum size, that means it has leading
+    // zeroes, and zeroes are not nines.
+    if (implicit_leading_zeroes > 0) {
+      trailing_nines = 0;
+    }
+
+    // But leading zeroes are zeroes (that could be trailing).
+    trailing_zeroes += implicit_leading_zeroes;
+  }
+
+  int digits_requested = (exp_precision + 1) - digits_checked;
+
+  int digits_to_check = digits_requested - implicit_leading_zeroes;
+  if (digits_to_check < 0) {
+    digits_to_check = 0;
+  }
+
+  // Check the upper digits of this block.
+  for (int i = 0; i < digits_to_check; ++i) {
+    if (int_to_str[i] == '9') {
+      ++trailing_nines;
+      trailing_zeroes = 0;
+    } else if (int_to_str[i] == '0') {
+      ++trailing_zeroes;
+      trailing_nines = 0;
+    } else {
+      trailing_nines = 0;
+      trailing_zeroes = 0;
+    }
+  }
+
+  // Find the digit after the lowest digit that we'll actually print to
+  // determine the rounding.
+  const uint32_t maximum = exp_precision + 1 - digits_checked;
+  uint32_t last_digit = 0;
+  for (uint32_t k = 0; k < last_block_size - maximum; ++k) {
+    last_digit = digits % 10;
+    digits /= 10;
+  }
+
+  // If the last block we read doesn't have the digit after the end of what
+  // we'll print, then we need to read the next block to get that digit.
+  if (maximum == last_block_size) {
+    BlockInt extra_block = float_converter.get_block(cur_block - 1);
+    last_digit = extra_block / ((MAX_BLOCK / 10) + 1);
+  }
+
+  // TODO: unify this code across the three float conversions.
+  RoundDirection round;
+  // Is m * 10^(additionalDigits + 1) / 2^(-exponent) integer?
+  const int32_t requiredTwos =
+      -exponent - MANT_WIDTH - static_cast<int32_t>(exp_precision) - 1;
+  // TODO: rename this variable to remove confusion with trailing_zeroes
+  const bool trailingZeros =
+      requiredTwos <= 0 ||
+      (requiredTwos < 60 &&
+       multiple_of_power_of_2(float_bits.get_explicit_mantissa(),
+                              static_cast<uint32_t>(requiredTwos)));
+  switch (fputil::get_round()) {
+  case FE_TONEAREST:
+    // Round to nearest, if it's exactly halfway then round to even.
+    if (last_digit != 5) {
+      round = last_digit > 5 ? RoundDirection::Up : RoundDirection::Down;
+    } else {
+      round = trailingZeros ? RoundDirection::Even : RoundDirection::Up;
+    }
+    break;
+  case FE_DOWNWARD:
+    if (is_negative && (!trailingZeros || last_digit > 0)) {
+      round = RoundDirection::Up;
+    } else {
+      round = RoundDirection::Down;
+    }
+    break;
+  case FE_UPWARD:
+    if (!is_negative && (!trailingZeros || last_digit > 0)) {
+      round = RoundDirection::Up;
+    } else {
+      round = RoundDirection::Down;
+    }
+    round = is_negative ? RoundDirection::Down : RoundDirection::Up;
+    break;
+  case FE_TOWARDZERO:
+    round = RoundDirection::Down;
+    break;
+  }
+
+  bool round_up;
+  if (round == RoundDirection::Up) {
+    round_up = true;
+  } else if (round == RoundDirection::Down) {
+    round_up = false;
+  } else {
+    // RoundDirection is even, so check the extra digit.
+    uint32_t low_digit = digits % 10;
+    round_up = (low_digit % 2) != 0;
+  }
+
+  digits_checked += digits_requested;
+  // assert(digits_checked == init_precision);
+  // At this point we should have checked all the digits requested by the
+  // precision. We may increment this number 1 more if we round up all of the
+  // digits, but at this point in the code digits_checked should always equal
+  // init_precision.
+
+  if (round_up) {
+    // If all the digits that would be printed are nines, then rounding up means
+    // that the base 10 exponent is one higher and all those nines turn to
+    // zeroes (e.g. 999 -> 1000).
+    if (trailing_nines == init_precision) {
+      ++base_10_exp;
+      trailing_zeroes = digits_checked;
+      ++digits_checked;
+    } else {
+      // If there are trailing nines, they turn into trailing zeroes when
+      // they're rounded up.
+      if (trailing_nines > 0) {
+        trailing_zeroes += trailing_nines;
+      } else if (trailing_zeroes > 0) {
+        // If there are trailing zeroes, then the last digit will be rounded up
+        // to a 1 so they aren't trailing anymore.
+        trailing_zeroes = 0;
+      }
+    }
+  }
+
+  // if P > X >= -4, the conversion is with style f (or F) and precision equals
+  //  P - (X + 1).
+  if (static_cast<int>(init_precision) > base_10_exp && base_10_exp >= -4) {
+    FormatSection new_conv = to_conv;
+    const size_t conv_precision = init_precision - (base_10_exp + 1);
+
+    if ((to_conv.flags & FormatFlags::ALTERNATE_FORM) != 0) {
+      new_conv.precision = conv_precision;
+    } else {
+      // If alt form isn't set, then we need to determine the number of trailing
+      // zeroes and set the precision such that they are removed.
+      int trimmed_precision =
+          digits_checked - (base_10_exp + 1) - trailing_zeroes;
+      if (trimmed_precision < 0) {
+        trimmed_precision = 0;
+      }
+      new_conv.precision =
+          (static_cast<size_t>(trimmed_precision) > conv_precision)
+              ? conv_precision
+              : trimmed_precision;
+    }
+
+    return convert_float_decimal_typed<T>(writer, new_conv, float_bits);
+  } else {
+    // otherwise, the conversion is with style e (or E) and precision equals
+    // P - 1
+    const size_t conv_precision = init_precision - 1;
+    FormatSection new_conv = to_conv;
+    if ((to_conv.flags & FormatFlags::ALTERNATE_FORM) != 0) {
+      new_conv.precision = conv_precision;
+    } else {
+      // If alt form isn't set, then we need to determine the number of trailing
+      // zeroes and set the precision such that they are removed.
+      int trimmed_precision = digits_checked - 1 - trailing_zeroes;
+      if (trimmed_precision < 0) {
+        trimmed_precision = 0;
+      }
+      new_conv.precision =
+          (static_cast<size_t>(trimmed_precision) > conv_precision)
+              ? conv_precision
+              : trimmed_precision;
+    }
+    return convert_float_dec_exp_typed<T>(writer, new_conv, float_bits);
+  }
+}
+
+// TODO: unify the float converters to remove the duplicated checks for inf/nan.
 LIBC_INLINE int convert_float_decimal(Writer *writer,
                                       const FormatSection &to_conv) {
   if (to_conv.length_modifier == LengthModifier::L) {
@@ -834,6 +1172,26 @@ LIBC_INLINE int convert_float_dec_exp(Writer *writer,
   return convert_inf_nan(writer, to_conv);
 }
 
+LIBC_INLINE int convert_float_dec_auto(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_auto_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_auto_typed<double>(writer, to_conv, float_bits);
+    }
+  }
+
+  return convert_inf_nan(writer, to_conv);
+}
+
 } // namespace printf_core
 } // namespace __llvm_libc
 

diff  --git a/libc/test/src/stdio/sprintf_test.cpp b/libc/test/src/stdio/sprintf_test.cpp
index 552cb70819368..8e16c889a1a4a 100644
--- a/libc/test/src/stdio/sprintf_test.cpp
+++ b/libc/test/src/stdio/sprintf_test.cpp
@@ -13,6 +13,11 @@
 #include "utils/UnitTest/Test.h"
 #include "utils/testutils/RoundingModeUtils.h"
 
+// #include <stdio.h>
+// namespace __llvm_libc {
+// using ::sprintf;
+// }
+
 class LlvmLibcSPrintfTest : public __llvm_libc::testing::Test {
 protected:
   char buff[1000];
@@ -1998,6 +2003,12 @@ TEST_F(LlvmLibcSPrintfTest, FloatExponentConv) {
   written = __llvm_libc::sprintf(buff, "%10.1e", 9.99);
   ASSERT_STREQ_LEN(written, buff, "   1.0e+01");
 
+  written = __llvm_libc::sprintf(buff, "%10.0e", 9.99);
+  ASSERT_STREQ_LEN(written, buff, "     1e+01");
+
+  written = __llvm_libc::sprintf(buff, "%10.0e", 0.0999);
+  ASSERT_STREQ_LEN(written, buff, "     1e-01");
+
   written = __llvm_libc::sprintf(buff, "%-10.2e", 9.99);
   ASSERT_STREQ_LEN(written, buff, "9.99e+00  ");
 
@@ -2019,6 +2030,9 @@ TEST_F(LlvmLibcSPrintfTest, FloatExponentConv) {
   written = __llvm_libc::sprintf(buff, "%25.13e", 9999999999999.99);
   ASSERT_STREQ_LEN(written, buff, "      1.0000000000000e+13");
 
+  written = __llvm_libc::sprintf(buff, "%25.12e", 9999999999999.99);
+  ASSERT_STREQ_LEN(written, buff, "       1.000000000000e+13");
+
   written = __llvm_libc::sprintf(buff, "%12.3e %-12.3e", 0.1, 256.0);
   ASSERT_STREQ_LEN(written, buff, "   1.000e-01 2.560e+02   ");
 
@@ -2026,6 +2040,628 @@ TEST_F(LlvmLibcSPrintfTest, FloatExponentConv) {
   ASSERT_STREQ_LEN(written, buff, "+1.256e-01    001.256e+03");
 }
 
+TEST_F(LlvmLibcSPrintfTest, FloatAutoConv) {
+  __llvm_libc::testutils::ForceRoundingMode r(
+      __llvm_libc::testutils::RoundingMode::Nearest);
+  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, "%g", 1.0);
+  ASSERT_STREQ_LEN(written, buff, "1");
+
+  written = __llvm_libc::sprintf(buff, "%G", -1.0);
+  ASSERT_STREQ_LEN(written, buff, "-1");
+
+  written = __llvm_libc::sprintf(buff, "%g", -1.234567);
+  ASSERT_STREQ_LEN(written, buff, "-1.23457");
+
+  written = __llvm_libc::sprintf(buff, "%g", 0.0);
+  ASSERT_STREQ_LEN(written, buff, "0");
+
+  written = __llvm_libc::sprintf(buff, "%g", -0.0);
+  ASSERT_STREQ_LEN(written, buff, "-0");
+
+  written = __llvm_libc::sprintf(buff, "%g", 1.5);
+  ASSERT_STREQ_LEN(written, buff, "1.5");
+
+  written = __llvm_libc::sprintf(buff, "%g", 1e300);
+  ASSERT_STREQ_LEN(written, buff, "1e+300");
+
+  written = __llvm_libc::sprintf(buff, "%g", 0.1);
+  ASSERT_STREQ_LEN(written, buff, "0.1");
+
+  written = __llvm_libc::sprintf(buff, "%g", 0.001);
+  ASSERT_STREQ_LEN(written, buff, "0.001");
+
+  written = __llvm_libc::sprintf(buff, "%g", 0.00001);
+  ASSERT_STREQ_LEN(written, buff, "1e-05");
+
+  written = __llvm_libc::sprintf(buff, "%g", 0.0000001);
+  ASSERT_STREQ_LEN(written, buff, "1e-07");
+
+  written = __llvm_libc::sprintf(buff, "%g", 0.000000001);
+  ASSERT_STREQ_LEN(written, buff, "1e-09");
+
+  written = __llvm_libc::sprintf(buff, "%g", 1.0e-20);
+  ASSERT_STREQ_LEN(written, buff, "1e-20");
+
+  written = __llvm_libc::sprintf(buff, "%g", 1234567890123456789.0);
+  ASSERT_STREQ_LEN(written, buff, "1.23457e+18");
+
+  written = __llvm_libc::sprintf(buff, "%g", 9999990000000.00);
+  ASSERT_STREQ_LEN(written, buff, "9.99999e+12");
+
+  written = __llvm_libc::sprintf(buff, "%g", 9999999000000.00);
+  ASSERT_STREQ_LEN(written, buff, "1e+13");
+
+  // Simple Subnormal Tests.
+
+  written = __llvm_libc::sprintf(buff, "%g", 0x1.0p-1027);
+  ASSERT_STREQ_LEN(written, buff, "6.95336e-310");
+
+  written = __llvm_libc::sprintf(buff, "%g", 0x1.0p-1074);
+  ASSERT_STREQ_LEN(written, buff, "4.94066e-324");
+
+  // Inf/Nan Tests.
+
+  written = __llvm_libc::sprintf(buff, "%g", inf);
+  ASSERT_STREQ_LEN(written, buff, "inf");
+
+  written = __llvm_libc::sprintf(buff, "%G", -inf);
+  ASSERT_STREQ_LEN(written, buff, "-INF");
+
+  written = __llvm_libc::sprintf(buff, "%g", nan);
+  ASSERT_STREQ_LEN(written, buff, "nan");
+
+  written = __llvm_libc::sprintf(buff, "%G", -nan);
+  ASSERT_STREQ_LEN(written, buff, "-NAN");
+
+  // Length Modifier Tests.
+
+  // TODO: Uncomment the below tests after long double support is added
+  /*
+  written = __llvm_libc::sprintf(buff, "%Lf", 1e100L);
+  ASSERT_STREQ_LEN(written, buff,
+                   "99999999999999999996693535322073426194986990198284960792713"
+                   "91541752018669482644324418977840117055488.000000");
+
+  written = __llvm_libc::sprintf(buff, "%Lf", 1.0L);
+  ASSERT_STREQ_LEN(written, buff, "1.000000");
+
+  char big_buff[10000];
+  written = __llvm_libc::sprintf(big_buff, "%Lf", 1e1000L);
+  ASSERT_STREQ_LEN(
+      written, big_buff,
+      "999999999999999999973107317669562353428234857594552594925899449376328728"
+      "202461036775511405481186963193066642191664822065529414252060696836533522"
+      "387143501724276282079456797058697369889056407118642873669166717313763499"
+      "277025985141177344925615052465165938514140943010597323750202561187880136"
+      "174810574553749194614479541820148407958204853833697063267336294787191005"
+      "628217462261955103745349844675732989944229689277833828743730290177882029"
+      "042613704915899149603539993716885598351951895974316347947147507970269673"
+      "097709017164643598452451201499004104341931127294141495501309305995449742"
+      "273419524803597130450457553871345958049837885085168840317195672271085085"
+      "950520957945970913451088104971436093671776829538796532762184174216651692"
+      "640931965387852083906784898823494867055070322768919156031682291829761007"
+      "101483799978382119231551218582499361996919560548090784230386907125151658"
+      "086767207295524036170321059257942621398084478974000973622199163292708506"
+      "2431457550909271560663602154947063707982236377366647567795879936."
+      "000000");
+
+  written = __llvm_libc::sprintf(big_buff, "%Lf", 1e4900L);
+  ASSERT_STREQ_LEN(
+      written, big_buff,
+      "100000000000000000002708312230690349833224052504078834346502930111959028"
+      "517260692666637048230414374897655201843766090626319971729765251179632020"
+      "313912652522792711197087872698264530532442630109549129842736280196919130"
+      "242615101228133188193853826983121366159061148351354364472807590931218045"
+      "387490935930967150336231085015126034696883068553581691802388371635128003"
+      "615577299166097675723780877126495909902479233742826339471026068806070433"
+      "075629449530819183550315434973800271862658869400009022028602967197463980"
+      "126881829804282202449930132940824361207087494829502385835258094836304011"
+      "876250359661206802659650567866176246063987902366800491980400341950657151"
+      "370854446585517805253310195469184699955519312761482572080479702840420595"
+      "377369017651259376039167277822106875560385309101650382998482652792335482"
+      "865443482342801545877390859444282105890147577937366066315975231014810320"
+      "888482059656248277607763361589359794524314002443575149260630989130103550"
+      "443177966380769341050735632338583912575890190136462629316287947355057647"
+      "111088565611192544631519843618778618820046304429723908484879583579178075"
+      "456701368334212923379389029311286386996015804122917416008806233549005183"
+      "152461084266176543129004016414959261473645240454289630182591200574019087"
+      "358223489767381636349719510715487188747217311279465814538495924567014916"
+      "238565628036285599497236493491668884212847699052761266207598941300449276"
+      "447201387520841811835583254242213093566548778954711633721122784159793843"
+      "766802019309395771984693609426401362800013936338891483689127845928572536"
+      "790651156184721483511507878883282891696900630100211914227950790472211403"
+      "392549466062537498185758854079775888444518306635752468713312357556380082"
+      "275500658967283696421824354930077523691855699312544373220921962817907078"
+      "445538421941800259027487429330768616490865438859612697367766323925013940"
+      "918384858952407145253573823848733994146335416209309233074165707437420756"
+      "438833918763109580759409985573826485055208965115587885226774453455112406"
+      "581351429640282227888764449360534584421929291565334894907337572527922691"
+      "473242328379737396430908523008687037407295838014450772162091496534584696"
+      "605157436893236842602956298545594095307060870397506421786236892553632163"
+      "491468601982681381011940409602294892199042638682530687578982576819839451"
+      "907594697546439533559153604700750696252355362322662219852740143212566818"
+      "745528402265116534684566273868361460640280523251242059850044328669692159"
+      "629900374576027104298177006629276014371540945261309319363704125592775129"
+      "543526908667388673739382491147471395192495459318806593271282662311169392"
+      "196897003517840025298267505925987901751541005546610016067658227181318892"
+      "914686508281007582655667597441346214499847364272258631922040641860333431"
+      "409838623713258383681350233064164940590695888300919626215847587544298023"
+      "636416943680102708406086295669759876682046839368574433996997648445207805"
+      "615784339667691231286807666753972942872019850432610318031627872612657513"
+      "588188267160616660825719678199868371370527508463011236193719286066916786"
+      "169956541349011494927225747024994619057884118692213564790598702879596058"
+      "672338334720925179141906809470606964896245458600635183723159228561689808"
+      "246141482736625197373238197777325580142168245885279594913851700941789475"
+      "252421784152262567254611571822468808675893407728003047921107885664474662"
+      "930921581384003950729114103689170603748380178682003976896397305836815761"
+      "717676338115866650889936516794601457549097578905329423919798362140648664"
+      "569177147076571576101649257502509463877402424847669830852345415301684820"
+      "395813946416649808062227494112874521812750160935760825922220707178083076"
+      "380203450993589198835885505461509442443773367592842795410339065860781804"
+      "024975272228687688301824830333940416256885455008512598774611538878683158"
+      "183931461086893832255176926531299425504132104728730288984598001187854507"
+      "900417184206801359847651992484444933900133130832052346600926424167009902"
+      "829803553087005800387704758687923428053612864451456596148162238935900033"
+      "917094683141205188616000211702577553792389670853917118547527592495253773"
+      "028135298405566315903922235989614934474805789300370437580494193066066314"
+      "056627605207631392651010580925826419831250810981343093764403877594495896"
+      "516881097415880926429607388979497471571321217205535961262051641426436441"
+      "668989765107456413733909427384182109285933511623871034309722437967253289"
+      "084018145083721513211807496392673952789642893241520398827805325610653506"
+      "029060153153064455898648607959013571280930834475689835845791849456112104"
+      "462337569019001580859906425911782967213265389744605395555069797947978230"
+      "708108432086217134763779632408473684293543722127232658767439906910370146"
+      "716836295909075482355827087389127370874842532825987593970846704144140471"
+      "956027276735614286138656432085771988513977140957180090146798065497158947"
+      "229765733489703157617307078835099906185890777007500964162371428641176460"
+      "739074789794941408428328217107759915202650066155868439585510978709442590"
+      "231934194956788626761834746430104077432547436359522462253411168467463134"
+      "24896.000000");
+*/
+  /*
+    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, "%15g", 1.0);
+  ASSERT_STREQ_LEN(written, buff, "              1");
+
+  written = __llvm_libc::sprintf(buff, "%15g", -1.0);
+  ASSERT_STREQ_LEN(written, buff, "             -1");
+
+  written = __llvm_libc::sprintf(buff, "%15g", 1.0e5);
+  ASSERT_STREQ_LEN(written, buff, "         100000");
+
+  written = __llvm_libc::sprintf(buff, "%15g", -1.0e5);
+  ASSERT_STREQ_LEN(written, buff, "        -100000");
+
+  written = __llvm_libc::sprintf(buff, "%10g", 1.0e-5);
+  ASSERT_STREQ_LEN(written, buff, "     1e-05");
+
+  // Precision Tests.
+
+  written = __llvm_libc::sprintf(buff, "%.2g", 1.23456789);
+  ASSERT_STREQ_LEN(written, buff, "1.2");
+
+  // Trimming trailing zeroes causes the precision to be ignored here.
+  written = __llvm_libc::sprintf(buff, "%.1g", 0.0);
+  ASSERT_STREQ_LEN(written, buff, "0");
+
+  written = __llvm_libc::sprintf(buff, "%.0g", 0.0);
+  ASSERT_STREQ_LEN(written, buff, "0");
+
+  written = __llvm_libc::sprintf(buff, "%.2g", 0.1);
+  ASSERT_STREQ_LEN(written, buff, "0.1");
+
+  written = __llvm_libc::sprintf(buff, "%.2g", 1.09);
+  ASSERT_STREQ_LEN(written, buff, "1.1");
+
+  written = __llvm_libc::sprintf(buff, "%.2g", 1.04);
+  ASSERT_STREQ_LEN(written, buff, "1");
+
+  written = __llvm_libc::sprintf(buff, "%.2g", 1.19);
+  ASSERT_STREQ_LEN(written, buff, "1.2");
+
+  written = __llvm_libc::sprintf(buff, "%.2g", 1.99);
+  ASSERT_STREQ_LEN(written, buff, "2");
+
+  written = __llvm_libc::sprintf(buff, "%.2g", 9.99);
+  ASSERT_STREQ_LEN(written, buff, "10");
+
+  written = __llvm_libc::sprintf(buff, "%.3g", 99.9);
+  ASSERT_STREQ_LEN(written, buff, "99.9");
+
+  written = __llvm_libc::sprintf(buff, "%.2g", 99.9);
+  ASSERT_STREQ_LEN(written, buff, "1e+02");
+
+  written = __llvm_libc::sprintf(buff, "%.1g", 99.9);
+  ASSERT_STREQ_LEN(written, buff, "1e+02");
+
+  written = __llvm_libc::sprintf(buff, "%.5g", 1.25);
+  ASSERT_STREQ_LEN(written, buff, "1.25");
+
+  written = __llvm_libc::sprintf(buff, "%.0g", 1.25);
+  ASSERT_STREQ_LEN(written, buff, "1");
+
+  written = __llvm_libc::sprintf(buff, "%.0g", 1.75);
+  ASSERT_STREQ_LEN(written, buff, "2");
+
+  written = __llvm_libc::sprintf(buff, "%.20g", 1.234e-10);
+  ASSERT_STREQ_LEN(written, buff, "1.2340000000000000814e-10");
+
+  written = __llvm_libc::sprintf(buff, "%.3g", -9.99);
+  ASSERT_STREQ_LEN(written, buff, "-9.99");
+
+  written = __llvm_libc::sprintf(buff, "%.2g", -9.99);
+  ASSERT_STREQ_LEN(written, buff, "-10");
+
+  written = __llvm_libc::sprintf(buff, "%.1g", -9.99);
+  ASSERT_STREQ_LEN(written, buff, "-1e+01");
+
+  written = __llvm_libc::sprintf(buff, "%.5g", 1.008);
+  ASSERT_STREQ_LEN(written, buff, "1.008");
+
+  written = __llvm_libc::sprintf(buff, "%.5g", 1.008e3);
+  ASSERT_STREQ_LEN(written, buff, "1008");
+
+  written = __llvm_libc::sprintf(buff, "%.4g", 9999.0);
+  ASSERT_STREQ_LEN(written, buff, "9999");
+
+  written = __llvm_libc::sprintf(buff, "%.3g", 9999.0);
+  ASSERT_STREQ_LEN(written, buff, "1e+04");
+
+  written = __llvm_libc::sprintf(buff, "%.3g", 1256.0);
+  ASSERT_STREQ_LEN(written, buff, "1.26e+03");
+
+  // Subnormal Precision Tests
+
+  written = __llvm_libc::sprintf(buff, "%.310g", 0x1.0p-1022);
+  ASSERT_STREQ_LEN(
+      written, buff,
+      "2."
+      "225073858507201383090232717332404064219215980462331830553327416887204434"
+      "813918195854283159012511020564067339731035811005152434161553460108856012"
+      "385377718821130777993532002330479610147442583636071921565046942503734208"
+      "375250806650616658158948720491179968591639648500635908770118304874799780"
+      "887753749949451580452e-308");
+
+  written = __llvm_libc::sprintf(buff, "%.30g", 0x1.0p-1022);
+  ASSERT_STREQ_LEN(written, buff, "2.22507385850720138309023271733e-308");
+
+  written = __llvm_libc::sprintf(buff, "%.310g", 0x1.0p-1023);
+  ASSERT_STREQ_LEN(
+      written, buff,
+      "1."
+      "112536929253600691545116358666202032109607990231165915276663708443602217"
+      "406959097927141579506255510282033669865517905502576217080776730054428006"
+      "192688859410565388996766001165239805073721291818035960782523471251867104"
+      "187625403325308329079474360245589984295819824250317954385059152437399890"
+      "443876874974725790226e-308");
+
+  written = __llvm_libc::sprintf(buff, "%.7g", 9.99999e-310);
+  ASSERT_STREQ_LEN(written, buff, "9.99999e-310");
+
+  written = __llvm_libc::sprintf(buff, "%.6g", 9.99999e-310);
+  ASSERT_STREQ_LEN(written, buff, "9.99999e-310");
+
+  written = __llvm_libc::sprintf(buff, "%.5g", 9.99999e-310);
+  ASSERT_STREQ_LEN(written, buff, "1e-309");
+
+  written = __llvm_libc::sprintf(buff, "%.4g", 9.99999e-310);
+  ASSERT_STREQ_LEN(written, buff, "1e-309");
+
+  written = __llvm_libc::sprintf(buff, "%.3g", 9.99999e-310);
+  ASSERT_STREQ_LEN(written, buff, "1e-309");
+
+  written = __llvm_libc::sprintf(buff, "%.2g", 9.99999e-310);
+  ASSERT_STREQ_LEN(written, buff, "1e-309");
+
+  written = __llvm_libc::sprintf(buff, "%.1g", 9.99999e-310);
+  ASSERT_STREQ_LEN(written, buff, "1e-309");
+
+  written = __llvm_libc::sprintf(buff, "%.0g", 9.99999e-310);
+  ASSERT_STREQ_LEN(written, buff, "1e-309");
+
+  written = __llvm_libc::sprintf(buff, "%.10g", 0x1.0p-1074);
+  ASSERT_STREQ_LEN(written, buff, "4.940656458e-324");
+
+  // Long double precision tests.
+  // These are currently commented out because they require long double support
+  // that isn't ready yet.
+  /*
+    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
+  */
+
+  // Rounding Mode Tests.
+
+  {
+    __llvm_libc::testutils::ForceRoundingMode r(
+        __llvm_libc::testutils::RoundingMode::Nearest);
+
+    written = __llvm_libc::sprintf(buff, "%.2g", 1.75);
+    ASSERT_STREQ_LEN(written, buff, "1.8");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", 1.25);
+    ASSERT_STREQ_LEN(written, buff, "1.2");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", 1.125);
+    ASSERT_STREQ_LEN(written, buff, "1.1");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", 1.625);
+    ASSERT_STREQ_LEN(written, buff, "1.6");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", 1.375);
+    ASSERT_STREQ_LEN(written, buff, "1.4");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", 1.875);
+    ASSERT_STREQ_LEN(written, buff, "1.9");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", -1.75);
+    ASSERT_STREQ_LEN(written, buff, "-1.8");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", -1.25);
+    ASSERT_STREQ_LEN(written, buff, "-1.2");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", -1.125);
+    ASSERT_STREQ_LEN(written, buff, "-1.1");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", -1.625);
+    ASSERT_STREQ_LEN(written, buff, "-1.6");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", -1.375);
+    ASSERT_STREQ_LEN(written, buff, "-1.4");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", -1.875);
+    ASSERT_STREQ_LEN(written, buff, "-1.9");
+  }
+
+  {
+    __llvm_libc::testutils::ForceRoundingMode r(
+        __llvm_libc::testutils::RoundingMode::Upward);
+
+    written = __llvm_libc::sprintf(buff, "%.2g", 1.75);
+    ASSERT_STREQ_LEN(written, buff, "1.8");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", 1.25);
+    ASSERT_STREQ_LEN(written, buff, "1.3");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", 1.125);
+    ASSERT_STREQ_LEN(written, buff, "1.2");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", 1.625);
+    ASSERT_STREQ_LEN(written, buff, "1.7");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", 1.375);
+    ASSERT_STREQ_LEN(written, buff, "1.4");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", 1.875);
+    ASSERT_STREQ_LEN(written, buff, "1.9");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", -1.75);
+    ASSERT_STREQ_LEN(written, buff, "-1.7");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", -1.25);
+    ASSERT_STREQ_LEN(written, buff, "-1.2");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", -1.125);
+    ASSERT_STREQ_LEN(written, buff, "-1.1");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", -1.625);
+    ASSERT_STREQ_LEN(written, buff, "-1.6");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", -1.375);
+    ASSERT_STREQ_LEN(written, buff, "-1.3");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", -1.875);
+    ASSERT_STREQ_LEN(written, buff, "-1.8");
+  }
+
+  {
+    __llvm_libc::testutils::ForceRoundingMode r(
+        __llvm_libc::testutils::RoundingMode::Downward);
+
+    written = __llvm_libc::sprintf(buff, "%.2g", 1.75);
+    ASSERT_STREQ_LEN(written, buff, "1.7");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", 1.25);
+    ASSERT_STREQ_LEN(written, buff, "1.2");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", 1.125);
+    ASSERT_STREQ_LEN(written, buff, "1.1");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", 1.625);
+    ASSERT_STREQ_LEN(written, buff, "1.6");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", 1.375);
+    ASSERT_STREQ_LEN(written, buff, "1.3");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", 1.875);
+    ASSERT_STREQ_LEN(written, buff, "1.8");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", -1.75);
+    ASSERT_STREQ_LEN(written, buff, "-1.8");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", -1.25);
+    ASSERT_STREQ_LEN(written, buff, "-1.3");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", -1.125);
+    ASSERT_STREQ_LEN(written, buff, "-1.2");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", -1.625);
+    ASSERT_STREQ_LEN(written, buff, "-1.7");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", -1.375);
+    ASSERT_STREQ_LEN(written, buff, "-1.4");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", -1.875);
+    ASSERT_STREQ_LEN(written, buff, "-1.9");
+  }
+
+  {
+    __llvm_libc::testutils::ForceRoundingMode r(
+        __llvm_libc::testutils::RoundingMode::TowardZero);
+
+    written = __llvm_libc::sprintf(buff, "%.2g", 1.75);
+    ASSERT_STREQ_LEN(written, buff, "1.7");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", 1.25);
+    ASSERT_STREQ_LEN(written, buff, "1.2");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", 1.125);
+    ASSERT_STREQ_LEN(written, buff, "1.1");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", 1.625);
+    ASSERT_STREQ_LEN(written, buff, "1.6");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", 1.375);
+    ASSERT_STREQ_LEN(written, buff, "1.3");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", 1.875);
+    ASSERT_STREQ_LEN(written, buff, "1.8");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", -1.75);
+    ASSERT_STREQ_LEN(written, buff, "-1.7");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", -1.25);
+    ASSERT_STREQ_LEN(written, buff, "-1.2");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", -1.125);
+    ASSERT_STREQ_LEN(written, buff, "-1.1");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", -1.625);
+    ASSERT_STREQ_LEN(written, buff, "-1.6");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", -1.375);
+    ASSERT_STREQ_LEN(written, buff, "-1.3");
+
+    written = __llvm_libc::sprintf(buff, "%.2g", -1.875);
+    ASSERT_STREQ_LEN(written, buff, "-1.8");
+  }
+
+  // Flag Tests.
+  written = __llvm_libc::sprintf(buff, "%+g", 1.0);
+  ASSERT_STREQ_LEN(written, buff, "+1");
+
+  written = __llvm_libc::sprintf(buff, "%+g", -1.0);
+  ASSERT_STREQ_LEN(written, buff, "-1");
+
+  written = __llvm_libc::sprintf(buff, "% g", 1.0);
+  ASSERT_STREQ_LEN(written, buff, " 1");
+
+  written = __llvm_libc::sprintf(buff, "% g", -1.0);
+  ASSERT_STREQ_LEN(written, buff, "-1");
+
+  written = __llvm_libc::sprintf(buff, "%-15g", 1.5);
+  ASSERT_STREQ_LEN(written, buff, "1.5            ");
+
+  written = __llvm_libc::sprintf(buff, "%#.g", 1.0);
+  ASSERT_STREQ_LEN(written, buff, "1.");
+
+  written = __llvm_libc::sprintf(buff, "%#g", 1.0);
+  ASSERT_STREQ_LEN(written, buff, "1.00000");
+
+  written = __llvm_libc::sprintf(buff, "%#.0g", 1.5);
+  ASSERT_STREQ_LEN(written, buff, "2.");
+
+  written = __llvm_libc::sprintf(buff, "%015g", 1.5);
+  ASSERT_STREQ_LEN(written, buff, "0000000000001.5");
+
+  written = __llvm_libc::sprintf(buff, "%015g", -1.5);
+  ASSERT_STREQ_LEN(written, buff, "-000000000001.5");
+
+  written = __llvm_libc::sprintf(buff, "%+- #0g", 0.0);
+  ASSERT_STREQ_LEN(written, buff, "+0.00000");
+
+  // Combined Tests.
+
+  written = __llvm_libc::sprintf(buff, "%10.3g", 9.99);
+  ASSERT_STREQ_LEN(written, buff, "      9.99");
+
+  written = __llvm_libc::sprintf(buff, "%10.2g", 9.99);
+  ASSERT_STREQ_LEN(written, buff, "        10");
+
+  written = __llvm_libc::sprintf(buff, "%10.1g", 9.99);
+  ASSERT_STREQ_LEN(written, buff, "     1e+01");
+
+  written = __llvm_libc::sprintf(buff, "%-10.3g", 9.99);
+  ASSERT_STREQ_LEN(written, buff, "9.99      ");
+
+  written = __llvm_libc::sprintf(buff, "%-10.2g", 9.99);
+  ASSERT_STREQ_LEN(written, buff, "10        ");
+
+  written = __llvm_libc::sprintf(buff, "%-10.1g", 9.99);
+  ASSERT_STREQ_LEN(written, buff, "1e+01     ");
+
+  written = __llvm_libc::sprintf(buff, "%-10.1g", 1.0e-50);
+  ASSERT_STREQ_LEN(written, buff, "1e-50     ");
+
+  written = __llvm_libc::sprintf(buff, "%30g", 1234567890123456789.0);
+  ASSERT_STREQ_LEN(written, buff, "                   1.23457e+18");
+
+  written = __llvm_libc::sprintf(buff, "%-30g", 1234567890123456789.0);
+  ASSERT_STREQ_LEN(written, buff, "1.23457e+18                   ");
+
+  written = __llvm_libc::sprintf(buff, "%25.15g", 9999999999999.99);
+  ASSERT_STREQ_LEN(written, buff, "         9999999999999.99");
+
+  written = __llvm_libc::sprintf(buff, "%25.14g", 9999999999999.99);
+  ASSERT_STREQ_LEN(written, buff, "           10000000000000");
+
+  written = __llvm_libc::sprintf(buff, "%25.13g", 9999999999999.99);
+  ASSERT_STREQ_LEN(written, buff, "                    1e+13");
+
+  written = __llvm_libc::sprintf(buff, "%#12.3g %-12.3g", 0.1, 256.0);
+  ASSERT_STREQ_LEN(written, buff, "       0.100 256         ");
+
+  written = __llvm_libc::sprintf(buff, "%+-#12.3g % 012.3g", 0.1256, 1256.0);
+  ASSERT_STREQ_LEN(written, buff, "+0.126        0001.26e+03");
+}
+
 #endif // LLVM_LIBC_PRINTF_DISABLE_FLOAT
 
 #ifndef LLVM_LIBC_PRINTF_DISABLE_WRITE_INT


        


More information about the libc-commits mailing list