[libc-commits] [libc] 5112651 - [libc] add exponent format to printf

Michael Jones via libc-commits libc-commits at lists.llvm.org
Thu Dec 22 10:21:05 PST 2022


Author: Michael Jones
Date: 2022-12-22T10:20:59-08:00
New Revision: 51126518261a061624110b013d6cc0a414488f5b

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

LOG: [libc] add exponent format to printf

Add support for the %e/E conversion in printf, as well as unit tests. It
does not yet support long doubles.

Reviewed By: sivachandra

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

Added: 
    

Modified: 
    libc/src/__support/float_to_string.h
    libc/src/stdio/printf_core/converter.cpp
    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 966c6dff4a0ba..874093505c99f 100644
--- a/libc/src/__support/float_to_string.h
+++ b/libc/src/__support/float_to_string.h
@@ -310,6 +310,15 @@ class FloatToString {
       return 0;
     }
   }
+
+  constexpr BlockInt get_block(int block_index) {
+    if (block_index >= 0) {
+      return get_positive_block(block_index);
+    } else {
+      return get_negative_block(-1 - block_index);
+    }
+  }
+
   constexpr size_t get_positive_blocks() {
     if (exponent >= -MANT_WIDTH) {
       const uint32_t idx =

diff  --git a/libc/src/stdio/printf_core/converter.cpp b/libc/src/stdio/printf_core/converter.cpp
index 458d86b00d1d4..2ceee7a56372c 100644
--- a/libc/src/stdio/printf_core/converter.cpp
+++ b/libc/src/stdio/printf_core/converter.cpp
@@ -46,9 +46,9 @@ int convert(Writer *writer, const FormatSection &to_conv) {
   case 'f':
   case 'F':
     return convert_float_decimal(writer, to_conv);
-  // case 'e':
-  // case 'E':
-  // return convert_float_dec_exp(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);

diff  --git a/libc/src/stdio/printf_core/float_dec_converter.h b/libc/src/stdio/printf_core/float_dec_converter.h
index c0af675ed44b8..9c7d3ad193562 100644
--- a/libc/src/stdio/printf_core/float_dec_converter.h
+++ b/libc/src/stdio/printf_core/float_dec_converter.h
@@ -12,6 +12,7 @@
 #include "src/__support/CPP/string_view.h"
 #include "src/__support/FPUtil/FEnvImpl.h"
 #include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/FPUtil/FloatProperties.h"
 #include "src/__support/UInt.h"
 #include "src/__support/UInt128.h"
 #include "src/__support/float_to_string.h"
@@ -106,16 +107,16 @@ class PaddingWriter {
   output when necessary.
 */
 class FloatWriter {
-  char block_buffer[BLOCK_SIZE];
-  size_t buffered_digits = 0;
-  bool has_written = false;
-  size_t max_block_count = 0;
-  size_t total_digits = 0;
-  size_t digits_before_decimal = 0;
-  size_t total_digits_written = 0;
-  bool has_decimal_point;
-  Writer *writer;
-  PaddingWriter padding_writer;
+  char block_buffer[BLOCK_SIZE]; // The buffer that holds a block.
+  size_t buffered_digits = 0;    // The number of digits held in the buffer.
+  bool has_written = false;      // True once any digits have been output.
+  size_t max_block_count = 0; // The # of blocks of all 9s currently buffered.
+  size_t total_digits = 0;    // The number of digits that will be output.
+  size_t digits_before_decimal = 0; // The # of digits to write before the '.'
+  size_t total_digits_written = 0;  // The # of digits that have been output.
+  bool has_decimal_point;           // True if the number has a decimal point.
+  Writer *writer;                   // Writes to the final output.
+  PaddingWriter padding_writer; // Handles prefixes/padding, uses total_digits.
 
   int flush_buffer() {
     // Write the most recent buffered block, and mark has_written
@@ -125,6 +126,31 @@ class FloatWriter {
           padding_writer.write_left_padding(writer, total_digits));
     }
 
+    // if the decimal point is the next character, or is in the range covered
+    // by the buffered block, write the appropriate digits and the decimal
+    // point.
+    if (total_digits_written < digits_before_decimal &&
+        total_digits_written + buffered_digits >= digits_before_decimal &&
+        has_decimal_point) {
+      size_t digits_to_write = digits_before_decimal - total_digits_written;
+      if (digits_to_write > 0) {
+        // Write the digits before the decimal point.
+        RET_IF_RESULT_NEGATIVE(writer->write({block_buffer, digits_to_write}));
+      }
+      RET_IF_RESULT_NEGATIVE(writer->write(DECIMAL_POINT));
+      if (buffered_digits - digits_to_write > 0) {
+        // Write the digits after the decimal point.
+        RET_IF_RESULT_NEGATIVE(
+            writer->write({block_buffer + digits_to_write,
+                           (buffered_digits - digits_to_write)}));
+      }
+      // add 1 for the decimal point
+      total_digits_written += buffered_digits + 1;
+      // Mark the buffer as empty.
+      buffered_digits = 0;
+    }
+
+    // Clear the buffered digits.
     if (buffered_digits > 0) {
       RET_IF_RESULT_NEGATIVE(writer->write({block_buffer, buffered_digits}));
       total_digits_written += buffered_digits;
@@ -133,7 +159,7 @@ class FloatWriter {
 
     // if the decimal point is the next character, or is in the range covered
     // by the max blocks, write the appropriate digits and the decimal point.
-    if (total_digits_written <= digits_before_decimal &&
+    if (total_digits_written < digits_before_decimal &&
         total_digits_written + BLOCK_SIZE * max_block_count >=
             digits_before_decimal &&
         has_decimal_point) {
@@ -142,9 +168,9 @@ class FloatWriter {
         RET_IF_RESULT_NEGATIVE(writer->write('9', digits_to_write));
       }
       RET_IF_RESULT_NEGATIVE(writer->write(DECIMAL_POINT));
-      if (digits_to_write - (BLOCK_SIZE * max_block_count) > 0) {
+      if ((BLOCK_SIZE * max_block_count) - digits_to_write > 0) {
         RET_IF_RESULT_NEGATIVE(writer->write(
-            '9', digits_to_write - (BLOCK_SIZE * max_block_count)));
+            '9', (BLOCK_SIZE * max_block_count) - digits_to_write));
       }
       // add 1 for the decimal point
       total_digits_written += BLOCK_SIZE * max_block_count + 1;
@@ -161,6 +187,40 @@ class FloatWriter {
     return 0;
   }
 
+  cpp::string_view exp_str(int exponent, cpp::span<char> exp_buffer) {
+
+    // -exponent will never overflow because all long double types we support
+    // have at most 15 bits of mantissa and the C standard defines an int as
+    // being at least 16 bits.
+    static_assert(fputil::FloatProperties<long double>::EXPONENT_WIDTH <
+                  (sizeof(int) * 8));
+
+    int positive_exponent = exponent < 0 ? -exponent : exponent;
+    char exp_sign = exponent < 0 ? '-' : '+';
+    auto const int_to_str =
+        *IntegerToString::dec(positive_exponent, exp_buffer);
+
+    // IntegerToString writes the digits from right to left so there will be
+    // space to the left of int_to_str.
+    size_t digits_in_exp = int_to_str.size();
+    size_t index = exp_buffer.size() - digits_in_exp - 1;
+
+    // Ensure that at least two digits were written. IntegerToString always
+    // writes at least 1 digit (it writes "0" when the input number is 0).
+    if (digits_in_exp < 2) {
+      exp_buffer[index] = '0';
+      --index;
+    }
+
+    // Since the exp_buffer has to be sized to handle an intmax_t, it has space
+    // for a sign. In this case we're handling the sign on our own since we also
+    // want plus signs for positive numbers.
+    exp_buffer[index] = exp_sign;
+
+    return cpp::string_view(exp_buffer.data() + index,
+                            exp_buffer.size() - index);
+  }
+
 public:
   FloatWriter(Writer *init_writer, bool init_has_decimal_point,
               const PaddingWriter &init_padding_writer)
@@ -172,20 +232,25 @@ class FloatWriter {
     digits_before_decimal = init_digits_before_decimal;
   }
 
-  void write_first_block(BlockInt block) {
+  void write_first_block(BlockInt block, bool exp_format = false) {
     char buf[IntegerToString::dec_bufsize<intmax_t>()];
     auto const int_to_str = *IntegerToString::dec(block, buf);
-    size_t digits_written = int_to_str.size();
+    size_t digits_buffered = int_to_str.size();
     // Block Buffer is guaranteed to not overflow since block cannot have more
     // than BLOCK_SIZE digits.
     // TODO: Replace with memcpy
-    for (size_t count = 0; count < digits_written; ++count) {
+    for (size_t count = 0; count < digits_buffered; ++count) {
       block_buffer[count] = int_to_str[count];
     }
-    total_digits_written = 0;
-    buffered_digits = digits_written;
-    total_digits += digits_written;
-    digits_before_decimal += digits_written;
+    buffered_digits = digits_buffered;
+
+    // In the exponent format (%e) we know how many digits will be written even
+    // before calculating any blocks, whereas the decimal format (%f) has to
+    // write all of the blocks that would come before the decimal place.
+    if (!exp_format) {
+      total_digits += digits_buffered;
+      digits_before_decimal += digits_buffered;
+    }
   }
 
   int write_middle_block(BlockInt block) {
@@ -260,7 +325,9 @@ class FloatWriter {
       if (has_carry /* && !has_written */) {
         ++total_digits;
         ++digits_before_decimal;
-        // TODO: Handle prefixes here
+        // 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
+        // can't be flushed.
         RET_IF_RESULT_NEGATIVE(
             padding_writer.write_left_padding(writer, total_digits));
         // Now we know we need to print a leading 1, zeroes up to the decimal
@@ -292,13 +359,124 @@ class FloatWriter {
     return 0;
   }
 
-  int write_last_block_exp(uint32_t block, size_t block_digits, int exponent) {
-    // TODO
-    //  This should be almost identical to the above, except in the case of
-    //  rounding all digits up. Instead of adding an extra digit in front of the
-    //  decimal point, we want to add 1 to the exponent.
-    // Also we need to write the exponent, but that's pretty simple.
-    return -1;
+  int write_last_block_exp(uint32_t block, size_t block_digits,
+                           RoundDirection round, int exponent, char exp_char) {
+    char end_buff[BLOCK_SIZE];
+
+    {
+      char buf[IntegerToString::dec_bufsize<intmax_t>()];
+      auto const int_to_str =
+          *IntegerToString::dec(block + (MAX_BLOCK + 1), buf);
+
+      // copy the last block_digits characters into the start of end_buff.
+      // TODO: Replace with memcpy
+      for (int count = block_digits - 1; count >= 0; --count) {
+        end_buff[count] = int_to_str[count + 1 + (BLOCK_SIZE - block_digits)];
+      }
+    }
+
+    char low_digit;
+    if (block_digits > 0) {
+      low_digit = end_buff[block_digits - 1];
+    } else if (max_block_count > 0) {
+      low_digit = '9';
+    } else {
+      low_digit = block_buffer[buffered_digits - 1];
+    }
+
+    // Round up
+    if (round == RoundDirection::Up ||
+        (round == RoundDirection::Even && low_digit % 2 != 0)) {
+      bool has_carry = true;
+      // handle the low block that we're adding
+      for (int count = block_digits - 1; count >= 0 && has_carry; --count) {
+        if (end_buff[count] == '9') {
+          end_buff[count] = '0';
+        } else {
+          end_buff[count] += 1;
+          has_carry = false;
+        }
+      }
+      // handle the high block that's buffered
+      for (int count = buffered_digits - 1; count >= 0 && has_carry; --count) {
+        if (block_buffer[count] == '9') {
+          block_buffer[count] = '0';
+        } else {
+          block_buffer[count] += 1;
+          has_carry = false;
+        }
+      }
+
+      // has_carry should only be true here if every previous digit is 9, which
+      // implies that the number has never been written.
+      if (has_carry /* && !has_written */) {
+        // Since this is exponential notation, we don't write any more digits
+        // but we do increment the exponent.
+        ++exponent;
+
+        char buf[IntegerToString::dec_bufsize<intmax_t>()];
+        auto const int_to_str = exp_str(exponent, buf);
+
+        // TODO: also change this to calculate the width of the number more
+        // efficiently.
+        size_t exponent_width = int_to_str.size();
+        size_t number_digits =
+            buffered_digits + (max_block_count * BLOCK_SIZE) + block_digits;
+
+        // 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;
+
+        // 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
+        // can't be flushed.
+        RET_IF_RESULT_NEGATIVE(
+            padding_writer.write_left_padding(writer, total_digits));
+        // Now we know we need to print a leading 1, the decimal point, and then
+        // zeroes after it.
+        RET_IF_RESULT_NEGATIVE(writer->write('1'));
+        // digits_before_decimal - 1 to account for the leading '1'
+        if (has_decimal_point) {
+          RET_IF_RESULT_NEGATIVE(writer->write(DECIMAL_POINT));
+          // This is just the length of the number, not including the decimal
+          // point, or exponent.
+
+          if (number_digits > 1) {
+            RET_IF_RESULT_NEGATIVE(writer->write('0', number_digits - 1));
+          }
+        }
+        RET_IF_RESULT_NEGATIVE(writer->write(exp_char));
+        RET_IF_RESULT_NEGATIVE(writer->write(int_to_str));
+
+        total_digits_written = total_digits;
+        return WRITE_OK;
+      }
+    }
+    // Either we intend to round down, or the rounding up is complete. Flush the
+    // buffers.
+
+    RET_IF_RESULT_NEGATIVE(flush_buffer());
+
+    // And then write the final block. It's written via the buffer so that if
+    // this is also the first block, the decimal point will be placed correctly.
+
+    // TODO: Replace with memcpy
+    for (size_t count = 0; count < block_digits; ++count) {
+      block_buffer[count] = end_buff[count];
+    }
+    buffered_digits = block_digits;
+    RET_IF_RESULT_NEGATIVE(flush_buffer());
+
+    char buf[IntegerToString::dec_bufsize<intmax_t>()];
+    auto const int_to_str = exp_str(exponent, buf);
+
+    RET_IF_RESULT_NEGATIVE(writer->write(exp_char));
+    RET_IF_RESULT_NEGATIVE(writer->write(int_to_str));
+
+    total_digits_written = total_digits;
+
+    return WRITE_OK;
   }
 
   int write_zeroes(uint32_t num_zeroes) {
@@ -333,7 +511,7 @@ int inline convert_float_decimal_typed(Writer *writer,
     sign_char = ' ';
 
   // If to_conv doesn't specify a precision, the precision defaults to 6.
-  size_t precision = to_conv.precision < 0 ? 6 : to_conv.precision;
+  const size_t precision = to_conv.precision < 0 ? 6 : to_conv.precision;
   bool has_decimal_point =
       (precision > 0) || ((to_conv.flags & FormatFlags::ALTERNATE_FORM) != 0);
 
@@ -456,6 +634,166 @@ int inline convert_float_decimal_typed(Writer *writer,
   return WRITE_OK;
 }
 
+template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
+int inline convert_float_dec_exp_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();
+
+  const char a = (to_conv.conv_name & 32) | 'A';
+
+  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 = ' ';
+
+  // If to_conv doesn't specify a precision, the precision defaults to 6.
+  const size_t precision = to_conv.precision < 0 ? 6 : to_conv.precision;
+  bool has_decimal_point =
+      (precision > 0) || ((to_conv.flags & FormatFlags::ALTERNATE_FORM) != 0);
+
+  PaddingWriter padding_writer(to_conv, sign_char);
+  FloatWriter float_writer(writer, has_decimal_point, padding_writer);
+  FloatToString<T> float_converter(static_cast<T>(float_bits));
+
+  size_t digits_written = 0;
+  int final_exponent = 0;
+
+  // 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. The first block is just 0.
+  if (mantissa != 0) {
+    // This loop finds the first block.
+    while (digits == 0) {
+      --cur_block;
+      digits = float_converter.get_block(cur_block);
+    }
+  } else {
+    cur_block = 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);
+  size_t block_width = int_to_str.size();
+
+  final_exponent = (cur_block * BLOCK_SIZE) + (block_width - 1);
+  int positive_exponent = final_exponent < 0 ? -final_exponent : final_exponent;
+
+  int_to_str = *IntegerToString::dec(positive_exponent, buf);
+  size_t exponent_width = int_to_str.size();
+
+  // Calculate the total number of digits in the number.
+  // 1 - the digit before the decimal point
+  // 1 - the decimal point (optional)
+  // precision - the number of digits after the decimal point
+  // 1 - the 'e' at the start of the exponent
+  // 1 - the sign at the start of the exponent
+  // max(2, exp width) - the digits of the exponent, min 2.
+
+  float_writer.init(1 + (has_decimal_point ? 1 : 0) + precision + 2 +
+                        (exponent_width < 2 ? 2 : exponent_width),
+                    1);
+
+  // If this block is not the last block
+  if (block_width <= precision + 1) {
+    float_writer.write_first_block(digits, true);
+    digits_written += block_width;
+    --cur_block;
+  }
+
+  // For each middle block.
+  for (; digits_written + BLOCK_SIZE < precision + 1; --cur_block) {
+    digits = float_converter.get_block(cur_block);
+
+    RET_IF_RESULT_NEGATIVE(float_writer.write_middle_block(digits));
+    digits_written += BLOCK_SIZE;
+  }
+
+  digits = float_converter.get_block(cur_block);
+
+  size_t last_block_size = BLOCK_SIZE;
+
+  // if the last block is also the first block, then ignore leading zeroes.
+  if (digits_written == 0) {
+    // TODO: Find a better way to calculate the number of digits in a block.
+    char buf[IntegerToString::dec_bufsize<intmax_t>()];
+    auto int_to_str = *IntegerToString::dec(digits, buf);
+    last_block_size = int_to_str.size();
+  }
+
+  // This is the last block.
+  const uint32_t maximum = precision + 1 - digits_written;
+  uint32_t lastDigit = 0;
+  for (uint32_t k = 0; k < last_block_size - maximum; ++k) {
+    lastDigit = 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;
+  const bool trailingZeros =
+      requiredTwos <= 0 ||
+      (requiredTwos < 60 &&
+       multiple_of_power_of_2(float_bits.get_explicit_mantissa(),
+                              (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;
+    } else {
+      round = trailingZeros ? RoundDirection::Even : RoundDirection::Up;
+    }
+    break;
+  case FE_DOWNWARD:
+    if (is_negative && (!trailingZeros || lastDigit > 0)) {
+      round = RoundDirection::Up;
+    } else {
+      round = RoundDirection::Down;
+    }
+    break;
+  case FE_UPWARD:
+    if (!is_negative && (!trailingZeros || lastDigit > 0)) {
+      round = RoundDirection::Up;
+    } else {
+      round = RoundDirection::Down;
+    }
+    round = is_negative ? RoundDirection::Down : RoundDirection::Up;
+    break;
+  case FE_TOWARDZERO:
+    round = RoundDirection::Down;
+    break;
+  }
+  RET_IF_RESULT_NEGATIVE(float_writer.write_last_block_exp(
+      digits, maximum, round, final_exponent, a + 'E' - 'A'));
+
+  RET_IF_RESULT_NEGATIVE(float_writer.right_pad());
+  return WRITE_OK;
+}
+
 int inline convert_float_decimal(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;
@@ -474,6 +812,26 @@ int inline convert_float_decimal(Writer *writer, const FormatSection &to_conv) {
 
   return convert_inf_nan(writer, to_conv);
 }
+
+int inline convert_float_dec_exp(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_exp_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_exp_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 73bad738b03e3..552cb70819368 100644
--- a/libc/test/src/stdio/sprintf_test.cpp
+++ b/libc/test/src/stdio/sprintf_test.cpp
@@ -1439,6 +1439,593 @@ TEST_F(LlvmLibcSPrintfTest, FloatDecimalConv) {
   written = __llvm_libc::sprintf(buff, "%+-#12.3f % 012.3f", 0.1256, 1256.0);
   ASSERT_STREQ_LEN(written, buff, "+0.126        0001256.000");
 }
+
+TEST_F(LlvmLibcSPrintfTest, FloatExponentConv) {
+  __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, "%e", 1.0);
+  ASSERT_STREQ_LEN(written, buff, "1.000000e+00");
+
+  written = __llvm_libc::sprintf(buff, "%E", -1.0);
+  ASSERT_STREQ_LEN(written, buff, "-1.000000E+00");
+
+  written = __llvm_libc::sprintf(buff, "%e", -1.234567);
+  ASSERT_STREQ_LEN(written, buff, "-1.234567e+00");
+
+  written = __llvm_libc::sprintf(buff, "%e", 0.0);
+  ASSERT_STREQ_LEN(written, buff, "0.000000e+00");
+
+  written = __llvm_libc::sprintf(buff, "%e", 1.5);
+  ASSERT_STREQ_LEN(written, buff, "1.500000e+00");
+
+  written = __llvm_libc::sprintf(buff, "%e", 1e300);
+  ASSERT_STREQ_LEN(written, buff, "1.000000e+300");
+
+  written = __llvm_libc::sprintf(buff, "%e", 0.1);
+  ASSERT_STREQ_LEN(written, buff, "1.000000e-01");
+
+  written = __llvm_libc::sprintf(buff, "%e", 0.001);
+  ASSERT_STREQ_LEN(written, buff, "1.000000e-03");
+
+  written = __llvm_libc::sprintf(buff, "%e", 0.00001);
+  ASSERT_STREQ_LEN(written, buff, "1.000000e-05");
+
+  written = __llvm_libc::sprintf(buff, "%e", 0.0000001);
+  ASSERT_STREQ_LEN(written, buff, "1.000000e-07");
+
+  written = __llvm_libc::sprintf(buff, "%e", 0.000000001);
+  ASSERT_STREQ_LEN(written, buff, "1.000000e-09");
+
+  written = __llvm_libc::sprintf(buff, "%e", 1.0e-20);
+  ASSERT_STREQ_LEN(written, buff, "1.000000e-20");
+
+  written = __llvm_libc::sprintf(buff, "%e", 1234567890123456789.0);
+  ASSERT_STREQ_LEN(written, buff, "1.234568e+18");
+
+  written = __llvm_libc::sprintf(buff, "%e", 9999999000000.00);
+  ASSERT_STREQ_LEN(written, buff, "9.999999e+12");
+
+  // Simple Subnormal Tests.
+
+  written = __llvm_libc::sprintf(buff, "%e", 0x1.0p-1027);
+  ASSERT_STREQ_LEN(written, buff, "6.953356e-310");
+
+  written = __llvm_libc::sprintf(buff, "%e", 0x1.0p-1074);
+  ASSERT_STREQ_LEN(written, buff, "4.940656e-324");
+
+  // Inf/Nan Tests.
+
+  written = __llvm_libc::sprintf(buff, "%e", inf);
+  ASSERT_STREQ_LEN(written, buff, "inf");
+
+  written = __llvm_libc::sprintf(buff, "%E", -inf);
+  ASSERT_STREQ_LEN(written, buff, "-INF");
+
+  written = __llvm_libc::sprintf(buff, "%e", nan);
+  ASSERT_STREQ_LEN(written, buff, "nan");
+
+  written = __llvm_libc::sprintf(buff, "%E", -nan);
+  ASSERT_STREQ_LEN(written, buff, "-NAN");
+
+  // Length Modifier Tests.
+
+  // TODO: Fix long doubles (needs bigger table or alternate algorithm.)
+  // Currently the table values are generated, which is very slow.
+  /*
+  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, "%15e", 1.0);
+  ASSERT_STREQ_LEN(written, buff, "   1.000000e+00");
+
+  written = __llvm_libc::sprintf(buff, "%15e", -1.0);
+  ASSERT_STREQ_LEN(written, buff, "  -1.000000e+00");
+
+  written = __llvm_libc::sprintf(buff, "%15e", 1.0e5);
+  ASSERT_STREQ_LEN(written, buff, "   1.000000e+05");
+
+  written = __llvm_libc::sprintf(buff, "%15e", -1.0e5);
+  ASSERT_STREQ_LEN(written, buff, "  -1.000000e+05");
+
+  written = __llvm_libc::sprintf(buff, "%10e", 1.0e-5);
+  ASSERT_STREQ_LEN(written, buff, "1.000000e-05");
+
+  // Precision Tests.
+
+  written = __llvm_libc::sprintf(buff, "%.1e", 1.0);
+  ASSERT_STREQ_LEN(written, buff, "1.0e+00");
+
+  written = __llvm_libc::sprintf(buff, "%.1e", 0.0);
+  ASSERT_STREQ_LEN(written, buff, "0.0e+00");
+
+  written = __llvm_libc::sprintf(buff, "%.0e", 0.0);
+  ASSERT_STREQ_LEN(written, buff, "0e+00");
+
+  written = __llvm_libc::sprintf(buff, "%.1e", 0.1);
+  ASSERT_STREQ_LEN(written, buff, "1.0e-01");
+
+  written = __llvm_libc::sprintf(buff, "%.1e", 1.09);
+  ASSERT_STREQ_LEN(written, buff, "1.1e+00");
+
+  written = __llvm_libc::sprintf(buff, "%.1e", 1.04);
+  ASSERT_STREQ_LEN(written, buff, "1.0e+00");
+
+  written = __llvm_libc::sprintf(buff, "%.1e", 1.19);
+  ASSERT_STREQ_LEN(written, buff, "1.2e+00");
+
+  written = __llvm_libc::sprintf(buff, "%.1e", 1.99);
+  ASSERT_STREQ_LEN(written, buff, "2.0e+00");
+
+  written = __llvm_libc::sprintf(buff, "%.1e", 9.99);
+  ASSERT_STREQ_LEN(written, buff, "1.0e+01");
+
+  written = __llvm_libc::sprintf(buff, "%.2e", 99.9);
+  ASSERT_STREQ_LEN(written, buff, "9.99e+01");
+
+  written = __llvm_libc::sprintf(buff, "%.1e", 99.9);
+  ASSERT_STREQ_LEN(written, buff, "1.0e+02");
+
+  written = __llvm_libc::sprintf(buff, "%.5e", 1.25);
+  ASSERT_STREQ_LEN(written, buff, "1.25000e+00");
+
+  written = __llvm_libc::sprintf(buff, "%.0e", 1.25);
+  ASSERT_STREQ_LEN(written, buff, "1e+00");
+
+  written = __llvm_libc::sprintf(buff, "%.0e", 1.75);
+  ASSERT_STREQ_LEN(written, buff, "2e+00");
+
+  written = __llvm_libc::sprintf(buff, "%.20e", 1.234e-10);
+  ASSERT_STREQ_LEN(written, buff, "1.23400000000000008140e-10");
+
+  written = __llvm_libc::sprintf(buff, "%.2e", -9.99);
+  ASSERT_STREQ_LEN(written, buff, "-9.99e+00");
+
+  written = __llvm_libc::sprintf(buff, "%.1e", -9.99);
+  ASSERT_STREQ_LEN(written, buff, "-1.0e+01");
+
+  written = __llvm_libc::sprintf(buff, "%.5e", 0.0);
+  ASSERT_STREQ_LEN(written, buff, "0.00000e+00");
+
+  written = __llvm_libc::sprintf(buff, "%.5e", 1.008);
+  ASSERT_STREQ_LEN(written, buff, "1.00800e+00");
+
+  written = __llvm_libc::sprintf(buff, "%.5e", 1.008e3);
+  ASSERT_STREQ_LEN(written, buff, "1.00800e+03");
+
+  // Subnormal Precision Tests
+
+  written = __llvm_libc::sprintf(buff, "%.310e", 0x1.0p-1022);
+  ASSERT_STREQ_LEN(
+      written, buff,
+      "2."
+      "225073858507201383090232717332404064219215980462331830553327416887204434"
+      "813918195854283159012511020564067339731035811005152434161553460108856012"
+      "385377718821130777993532002330479610147442583636071921565046942503734208"
+      "375250806650616658158948720491179968591639648500635908770118304874799780"
+      "8877537499494515804516e-308");
+
+  written = __llvm_libc::sprintf(buff, "%.30e", 0x1.0p-1022);
+  ASSERT_STREQ_LEN(written, buff, "2.225073858507201383090232717332e-308");
+
+  written = __llvm_libc::sprintf(buff, "%.310e", 0x1.0p-1023);
+  ASSERT_STREQ_LEN(
+      written, buff,
+      "1."
+      "112536929253600691545116358666202032109607990231165915276663708443602217"
+      "406959097927141579506255510282033669865517905502576217080776730054428006"
+      "192688859410565388996766001165239805073721291818035960782523471251867104"
+      "187625403325308329079474360245589984295819824250317954385059152437399890"
+      "4438768749747257902258e-308");
+
+  written = __llvm_libc::sprintf(buff, "%.6e", 9.99999e-310);
+  ASSERT_STREQ_LEN(written, buff, "9.999990e-310");
+
+  written = __llvm_libc::sprintf(buff, "%.5e", 9.99999e-310);
+  ASSERT_STREQ_LEN(written, buff, "9.99999e-310");
+
+  written = __llvm_libc::sprintf(buff, "%.4e", 9.99999e-310);
+  ASSERT_STREQ_LEN(written, buff, "1.0000e-309");
+
+  written = __llvm_libc::sprintf(buff, "%.3e", 9.99999e-310);
+  ASSERT_STREQ_LEN(written, buff, "1.000e-309");
+
+  written = __llvm_libc::sprintf(buff, "%.2e", 9.99999e-310);
+  ASSERT_STREQ_LEN(written, buff, "1.00e-309");
+
+  written = __llvm_libc::sprintf(buff, "%.1e", 9.99999e-310);
+  ASSERT_STREQ_LEN(written, buff, "1.0e-309");
+
+  written = __llvm_libc::sprintf(buff, "%.0e", 9.99999e-310);
+  ASSERT_STREQ_LEN(written, buff, "1e-309");
+
+  written = __llvm_libc::sprintf(buff, "%.10e", 0x1.0p-1074);
+  ASSERT_STREQ_LEN(written, buff, "4.9406564584e-324");
+
+  /*
+    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, "%.1e", 1.75);
+    ASSERT_STREQ_LEN(written, buff, "1.8e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", 1.25);
+    ASSERT_STREQ_LEN(written, buff, "1.2e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", 1.125);
+    ASSERT_STREQ_LEN(written, buff, "1.1e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", 1.625);
+    ASSERT_STREQ_LEN(written, buff, "1.6e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", 1.375);
+    ASSERT_STREQ_LEN(written, buff, "1.4e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", 1.875);
+    ASSERT_STREQ_LEN(written, buff, "1.9e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", -1.75);
+    ASSERT_STREQ_LEN(written, buff, "-1.8e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", -1.25);
+    ASSERT_STREQ_LEN(written, buff, "-1.2e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", -1.125);
+    ASSERT_STREQ_LEN(written, buff, "-1.1e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", -1.625);
+    ASSERT_STREQ_LEN(written, buff, "-1.6e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", -1.375);
+    ASSERT_STREQ_LEN(written, buff, "-1.4e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", -1.875);
+    ASSERT_STREQ_LEN(written, buff, "-1.9e+00");
+  }
+
+  {
+    __llvm_libc::testutils::ForceRoundingMode r(
+        __llvm_libc::testutils::RoundingMode::Upward);
+
+    written = __llvm_libc::sprintf(buff, "%.1e", 1.75);
+    ASSERT_STREQ_LEN(written, buff, "1.8e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", 1.25);
+    ASSERT_STREQ_LEN(written, buff, "1.3e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", 1.125);
+    ASSERT_STREQ_LEN(written, buff, "1.2e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", 1.625);
+    ASSERT_STREQ_LEN(written, buff, "1.7e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", 1.375);
+    ASSERT_STREQ_LEN(written, buff, "1.4e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", 1.875);
+    ASSERT_STREQ_LEN(written, buff, "1.9e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", -1.75);
+    ASSERT_STREQ_LEN(written, buff, "-1.7e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", -1.25);
+    ASSERT_STREQ_LEN(written, buff, "-1.2e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", -1.125);
+    ASSERT_STREQ_LEN(written, buff, "-1.1e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", -1.625);
+    ASSERT_STREQ_LEN(written, buff, "-1.6e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", -1.375);
+    ASSERT_STREQ_LEN(written, buff, "-1.3e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", -1.875);
+    ASSERT_STREQ_LEN(written, buff, "-1.8e+00");
+  }
+
+  {
+    __llvm_libc::testutils::ForceRoundingMode r(
+        __llvm_libc::testutils::RoundingMode::Downward);
+
+    written = __llvm_libc::sprintf(buff, "%.1e", 1.75);
+    ASSERT_STREQ_LEN(written, buff, "1.7e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", 1.25);
+    ASSERT_STREQ_LEN(written, buff, "1.2e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", 1.125);
+    ASSERT_STREQ_LEN(written, buff, "1.1e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", 1.625);
+    ASSERT_STREQ_LEN(written, buff, "1.6e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", 1.375);
+    ASSERT_STREQ_LEN(written, buff, "1.3e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", 1.875);
+    ASSERT_STREQ_LEN(written, buff, "1.8e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", -1.75);
+    ASSERT_STREQ_LEN(written, buff, "-1.8e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", -1.25);
+    ASSERT_STREQ_LEN(written, buff, "-1.3e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", -1.125);
+    ASSERT_STREQ_LEN(written, buff, "-1.2e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", -1.625);
+    ASSERT_STREQ_LEN(written, buff, "-1.7e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", -1.375);
+    ASSERT_STREQ_LEN(written, buff, "-1.4e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", -1.875);
+    ASSERT_STREQ_LEN(written, buff, "-1.9e+00");
+  }
+
+  {
+    __llvm_libc::testutils::ForceRoundingMode r(
+        __llvm_libc::testutils::RoundingMode::TowardZero);
+
+    written = __llvm_libc::sprintf(buff, "%.1e", 1.75);
+    ASSERT_STREQ_LEN(written, buff, "1.7e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", 1.25);
+    ASSERT_STREQ_LEN(written, buff, "1.2e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", 1.125);
+    ASSERT_STREQ_LEN(written, buff, "1.1e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", 1.625);
+    ASSERT_STREQ_LEN(written, buff, "1.6e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", 1.375);
+    ASSERT_STREQ_LEN(written, buff, "1.3e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", 1.875);
+    ASSERT_STREQ_LEN(written, buff, "1.8e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", -1.75);
+    ASSERT_STREQ_LEN(written, buff, "-1.7e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", -1.25);
+    ASSERT_STREQ_LEN(written, buff, "-1.2e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", -1.125);
+    ASSERT_STREQ_LEN(written, buff, "-1.1e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", -1.625);
+    ASSERT_STREQ_LEN(written, buff, "-1.6e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", -1.375);
+    ASSERT_STREQ_LEN(written, buff, "-1.3e+00");
+
+    written = __llvm_libc::sprintf(buff, "%.1e", -1.875);
+    ASSERT_STREQ_LEN(written, buff, "-1.8e+00");
+  }
+
+  // Flag Tests.
+  written = __llvm_libc::sprintf(buff, "%+e", 1.0);
+  ASSERT_STREQ_LEN(written, buff, "+1.000000e+00");
+
+  written = __llvm_libc::sprintf(buff, "%+e", -1.0);
+  ASSERT_STREQ_LEN(written, buff, "-1.000000e+00");
+
+  written = __llvm_libc::sprintf(buff, "% e", 1.0);
+  ASSERT_STREQ_LEN(written, buff, " 1.000000e+00");
+
+  written = __llvm_libc::sprintf(buff, "% e", -1.0);
+  ASSERT_STREQ_LEN(written, buff, "-1.000000e+00");
+
+  written = __llvm_libc::sprintf(buff, "%-15e", 1.5);
+  ASSERT_STREQ_LEN(written, buff, "1.500000e+00   ");
+
+  written = __llvm_libc::sprintf(buff, "%#.e", 1.0);
+  ASSERT_STREQ_LEN(written, buff, "1.e+00");
+
+  written = __llvm_libc::sprintf(buff, "%#.0e", 1.5);
+  ASSERT_STREQ_LEN(written, buff, "2.e+00");
+
+  written = __llvm_libc::sprintf(buff, "%015e", 1.5);
+  ASSERT_STREQ_LEN(written, buff, "0001.500000e+00");
+
+  written = __llvm_libc::sprintf(buff, "%015e", -1.5);
+  ASSERT_STREQ_LEN(written, buff, "-001.500000e+00");
+
+  written = __llvm_libc::sprintf(buff, "%+- #0e", 0.0);
+  ASSERT_STREQ_LEN(written, buff, "+0.000000e+00");
+
+  // Combined Tests.
+
+  written = __llvm_libc::sprintf(buff, "%10.2e", 9.99);
+  ASSERT_STREQ_LEN(written, buff, "  9.99e+00");
+
+  written = __llvm_libc::sprintf(buff, "%10.1e", 9.99);
+  ASSERT_STREQ_LEN(written, buff, "   1.0e+01");
+
+  written = __llvm_libc::sprintf(buff, "%-10.2e", 9.99);
+  ASSERT_STREQ_LEN(written, buff, "9.99e+00  ");
+
+  written = __llvm_libc::sprintf(buff, "%-10.1e", 9.99);
+  ASSERT_STREQ_LEN(written, buff, "1.0e+01   ");
+
+  written = __llvm_libc::sprintf(buff, "%-10.1e", 1.0e-50);
+  ASSERT_STREQ_LEN(written, buff, "1.0e-50   ");
+
+  written = __llvm_libc::sprintf(buff, "%30e", 1234567890123456789.0);
+  ASSERT_STREQ_LEN(written, buff, "                  1.234568e+18");
+
+  written = __llvm_libc::sprintf(buff, "%-30e", 1234567890123456789.0);
+  ASSERT_STREQ_LEN(written, buff, "1.234568e+18                  ");
+
+  written = __llvm_libc::sprintf(buff, "%25.14e", 9999999999999.99);
+  ASSERT_STREQ_LEN(written, buff, "     9.99999999999999e+12");
+
+  written = __llvm_libc::sprintf(buff, "%25.13e", 9999999999999.99);
+  ASSERT_STREQ_LEN(written, buff, "      1.0000000000000e+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   ");
+
+  written = __llvm_libc::sprintf(buff, "%+-#12.3e % 012.3e", 0.1256, 1256.0);
+  ASSERT_STREQ_LEN(written, buff, "+1.256e-01    001.256e+03");
+}
+
 #endif // LLVM_LIBC_PRINTF_DISABLE_FLOAT
 
 #ifndef LLVM_LIBC_PRINTF_DISABLE_WRITE_INT


        


More information about the libc-commits mailing list