Resubmit "Add support for advanced number formatting."
authorZachary Turner <zturner@google.com>
Sat, 29 Oct 2016 00:27:22 +0000 (00:27 +0000)
committerZachary Turner <zturner@google.com>
Sat, 29 Oct 2016 00:27:22 +0000 (00:27 +0000)
This resubmits r284436 and r284437, which were reverted in
r284462 as they were breaking the AArch64 buildbot.

The breakage on AArch64 turned out to be a miscompile which is
still not fixed, but is actively tracked at llvm.org/pr30748.

This resubmission re-writes the code in a way so as to make the
miscompile not happen.

llvm-svn: 285483

llvm/include/llvm/Support/NativeFormatting.h
llvm/lib/Support/NativeFormatting.cpp
llvm/lib/Support/raw_ostream.cpp
llvm/unittests/Support/CMakeLists.txt
llvm/unittests/Support/NativeFormatTests.cpp [new file with mode: 0644]

index 6f7e077..56da163 100644 (file)
 #ifndef LLVM_SUPPORT_NATIVE_FORMATTING_H
 #define LLVM_SUPPORT_NATIVE_FORMATTING_H
 
+#include "llvm/ADT/Optional.h"
 #include "llvm/Support/raw_ostream.h"
 
 #include <cstdint>
 
 namespace llvm {
-enum class FloatStyle { Exponent, Decimal };
-
-void write_ulong(raw_ostream &S, unsigned long N, std::size_t MinWidth);
-void write_long(raw_ostream &S, long N, std::size_t MinWidth);
-void write_ulonglong(raw_ostream &S, unsigned long long N,
-                     std::size_t MinWidth);
-void write_longlong(raw_ostream &S, long long N, std::size_t MinWidth);
-void write_hex(raw_ostream &S, unsigned long long N, std::size_t MinWidth,
-               bool Upper, bool Prefix);
-void write_double(raw_ostream &S, double D, std::size_t MinWidth,
-                  std::size_t MinDecimals, FloatStyle Style);
+enum class FloatStyle { Exponent, ExponentUpper, Fixed, Percent };
+enum class IntegerStyle {
+  Integer,
+  Number,
+};
+enum class HexPrintStyle { Upper, Lower, PrefixUpper, PrefixLower };
+
+size_t getDefaultPrecision(FloatStyle Style);
+
+void write_integer(raw_ostream &S, unsigned int N, IntegerStyle Style);
+void write_integer(raw_ostream &S, int N, IntegerStyle Style);
+void write_integer(raw_ostream &S, unsigned long N, IntegerStyle Style);
+void write_integer(raw_ostream &S, long N, IntegerStyle Style);
+void write_integer(raw_ostream &S, unsigned long long N, IntegerStyle Style);
+void write_integer(raw_ostream &S, long long N, IntegerStyle Style);
+
+void write_hex(raw_ostream &S, uint64_t N, HexPrintStyle Style,
+               Optional<size_t> Width = None);
+void write_double(raw_ostream &S, double D, FloatStyle Style,
+                  Optional<size_t> Precision = None);
 }
 
 #endif
\ No newline at end of file
index 9989488..bbbb1fb 100644 (file)
@@ -9,6 +9,7 @@
 
 #include "llvm/Support/NativeFormatting.h"
 
+#include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/Format.h"
@@ -20,96 +21,121 @@ static int format_to_buffer(T Value, char (&Buffer)[N]) {
   char *EndPtr = std::end(Buffer);
   char *CurPtr = EndPtr;
 
-  while (Value) {
+  do {
     *--CurPtr = '0' + char(Value % 10);
     Value /= 10;
-  }
+  } while (Value);
   return EndPtr - CurPtr;
 }
 
-void llvm::write_ulong(raw_ostream &S, unsigned long N, std::size_t MinWidth) {
-  // Zero is a special case.
-  if (N == 0) {
-    if (MinWidth > 0)
-      S.indent(MinWidth - 1);
-    S << '0';
-    return;
+static void writeWithCommas(raw_ostream &S, ArrayRef<char> Buffer) {
+  assert(!Buffer.empty());
+
+  ArrayRef<char> ThisGroup;
+  int InitialDigits = ((Buffer.size() - 1) % 3) + 1;
+  ThisGroup = Buffer.take_front(InitialDigits);
+  S.write(ThisGroup.data(), ThisGroup.size());
+
+  Buffer = Buffer.drop_front(InitialDigits);
+  assert(Buffer.size() % 3 == 0);
+  while (!Buffer.empty()) {
+    S << ',';
+    ThisGroup = Buffer.take_front(3);
+    S.write(ThisGroup.data(), 3);
+    Buffer = Buffer.drop_front(3);
   }
+}
+
+template <typename T>
+static void write_unsigned_impl(raw_ostream &S, T N, IntegerStyle Style,
+                                bool IsNegative) {
+  static_assert(std::is_unsigned<T>::value, "Value is not unsigned!");
+
+  char NumberBuffer[128];
+  std::memset(NumberBuffer, '0', sizeof(NumberBuffer));
+
+  size_t Len = 0;
+  Len = format_to_buffer(N, NumberBuffer);
+
+  if (IsNegative)
+    S << '-';
+  if (Style == IntegerStyle::Number) {
+    writeWithCommas(S, ArrayRef<char>(std::end(NumberBuffer) - Len, Len));
+  } else {
+    S.write(std::end(NumberBuffer) - Len, Len);
+  }
+}
 
-  char NumberBuffer[20];
-  int Len = format_to_buffer(N, NumberBuffer);
-  int Pad = (MinWidth == 0) ? 0 : MinWidth - Len;
-  if (Pad > 0)
-    S.indent(Pad);
-  S.write(std::end(NumberBuffer) - Len, Len);
+template <typename T>
+static void write_unsigned(raw_ostream &S, T N, IntegerStyle Style,
+                           bool IsNegative = false) {
+  // Output using 32-bit div/mod if possible.
+  if (N == static_cast<uint32_t>(N))
+    write_unsigned_impl(S, static_cast<uint32_t>(N), Style, IsNegative);
+  else
+    write_unsigned_impl(S, N, Style, IsNegative);
 }
 
-void llvm::write_long(raw_ostream &S, long N, std::size_t MinWidth) {
+template <typename T>
+static void write_signed(raw_ostream &S, T N, IntegerStyle Style) {
+  static_assert(std::is_signed<T>::value, "Value is not signed!");
+
+  using UnsignedT = typename std::make_unsigned<T>::type;
+
   if (N >= 0) {
-    write_ulong(S, static_cast<unsigned long>(N), MinWidth);
+    write_unsigned(S, static_cast<UnsignedT>(N), Style);
     return;
   }
 
-  unsigned long UN = -(unsigned long)N;
-  if (MinWidth > 0)
-    --MinWidth;
-
-  char NumberBuffer[20];
-  int Len = format_to_buffer(UN, NumberBuffer);
-  int Pad = (MinWidth == 0) ? 0 : MinWidth - Len;
-  if (Pad > 0)
-    S.indent(Pad);
-  S.write('-');
-  S.write(std::end(NumberBuffer) - Len, Len);
+  UnsignedT UN = -(UnsignedT)N;
+  write_unsigned(S, UN, Style, true);
 }
 
-void llvm::write_ulonglong(raw_ostream &S, unsigned long long N,
-                           std::size_t MinWidth) {
-  // Output using 32-bit div/mod when possible.
-  if (N == static_cast<unsigned long>(N)) {
-    write_ulong(S, static_cast<unsigned long>(N), MinWidth);
-    return;
-  }
+void llvm::write_integer(raw_ostream &S, unsigned int N, IntegerStyle Style) {
+  write_unsigned(S, N, Style);
+}
 
-  char NumberBuffer[32];
-  int Len = format_to_buffer(N, NumberBuffer);
-  int Pad = (MinWidth == 0) ? 0 : MinWidth - Len;
-  if (Pad > 0)
-    S.indent(Pad);
-  S.write(std::end(NumberBuffer) - Len, Len);
+void llvm::write_integer(raw_ostream &S, int N, IntegerStyle Style) {
+  write_signed(S, N, Style);
 }
 
-void llvm::write_longlong(raw_ostream &S, long long N, std::size_t MinWidth) {
-  if (N >= 0) {
-    write_ulonglong(S, static_cast<unsigned long long>(N), MinWidth);
-    return;
-  }
+void llvm::write_integer(raw_ostream &S, unsigned long N, IntegerStyle Style) {
+  write_unsigned(S, N, Style);
+}
 
-  // Avoid undefined behavior on INT64_MIN with a cast.
-  unsigned long long UN = -(unsigned long long)N;
-  if (MinWidth > 0)
-    --MinWidth;
-
-  char NumberBuffer[32];
-  int Len = format_to_buffer(UN, NumberBuffer);
-  int Pad = (MinWidth == 0) ? 0 : MinWidth - Len;
-  if (Pad > 0)
-    S.indent(Pad);
-  S.write('-');
-  S.write(std::end(NumberBuffer) - Len, Len);
+void llvm::write_integer(raw_ostream &S, long N, IntegerStyle Style) {
+  write_signed(S, N, Style);
 }
 
-void llvm::write_hex(raw_ostream &S, unsigned long long N, std::size_t MinWidth,
-                     bool Upper, bool Prefix) {
+void llvm::write_integer(raw_ostream &S, unsigned long long N,
+                         IntegerStyle Style) {
+  write_unsigned(S, N, Style);
+}
+
+void llvm::write_integer(raw_ostream &S, long long N, IntegerStyle Style) {
+  write_signed(S, N, Style);
+}
+
+void llvm::write_hex(raw_ostream &S, uint64_t N, HexPrintStyle Style,
+                     Optional<size_t> Width) {
+  const size_t kMaxWidth = 128u;
+
+  size_t W = std::min(kMaxWidth, Width.getValueOr(0u));
+
   unsigned Nibbles = (64 - countLeadingZeros(N) + 3) / 4;
+  bool Prefix = (Style == HexPrintStyle::PrefixLower ||
+                 Style == HexPrintStyle::PrefixUpper);
+  bool Upper =
+      (Style == HexPrintStyle::Upper || Style == HexPrintStyle::PrefixUpper);
   unsigned PrefixChars = Prefix ? 2 : 0;
-  unsigned Width = std::max(static_cast<unsigned>(MinWidth),
-                            std::max(1u, Nibbles) + PrefixChars);
-
-  char NumberBuffer[20] = "0x0000000000000000";
-  if (!Prefix)
-    NumberBuffer[1] = '0';
-  char *EndPtr = NumberBuffer + Width;
+  unsigned NumChars =
+      std::max(static_cast<unsigned>(W), std::max(1u, Nibbles) + PrefixChars);
+
+  char NumberBuffer[kMaxWidth];
+  ::memset(NumberBuffer, '0', llvm::array_lengthof(NumberBuffer));
+  if (Prefix)
+    NumberBuffer[1] = 'x';
+  char *EndPtr = NumberBuffer + NumChars;
   char *CurPtr = EndPtr;
   while (N) {
     unsigned char x = static_cast<unsigned char>(N) % 16;
@@ -117,22 +143,34 @@ void llvm::write_hex(raw_ostream &S, unsigned long long N, std::size_t MinWidth,
     N /= 16;
   }
 
-  S.write(NumberBuffer, Width);
+  S.write(NumberBuffer, NumChars);
 }
 
-void llvm::write_double(raw_ostream &S, double N, std::size_t MinWidth,
-                        std::size_t MinDecimals, FloatStyle Style) {
-  char Letter = (Style == FloatStyle::Exponent) ? 'e' : 'f';
+void llvm::write_double(raw_ostream &S, double N, FloatStyle Style,
+                        Optional<size_t> Precision) {
+  size_t Prec = Precision.getValueOr(getDefaultPrecision(Style));
+
+  if (std::isnan(N)) {
+    S << "nan";
+    return;
+  } else if (std::isinf(N)) {
+    S << "INF";
+    return;
+  }
+
+  char Letter;
+  if (Style == FloatStyle::Exponent)
+    Letter = 'e';
+  else if (Style == FloatStyle::ExponentUpper)
+    Letter = 'E';
+  else
+    Letter = 'f';
+
   SmallString<8> Spec;
   llvm::raw_svector_ostream Out(Spec);
-  Out << '%';
-  if (MinWidth > 0)
-    Out << MinWidth;
-  if (MinDecimals > 0)
-    Out << '.' << MinDecimals;
-  Out << Letter;
-
-  if (Style == FloatStyle::Exponent) {
+  Out << "%." << Prec << Letter;
+
+  if (Style == FloatStyle::Exponent || Style == FloatStyle::ExponentUpper) {
 #ifdef _WIN32
 // On MSVCRT and compatible, output of %e is incompatible to Posix
 // by default. Number of exponent digits should be at least 2. "%+03d"
@@ -140,7 +178,9 @@ void llvm::write_double(raw_ostream &S, double N, std::size_t MinWidth,
 #if defined(__MINGW32__)
     // FIXME: It should be generic to C++11.
     if (N == 0.0 && std::signbit(N)) {
-      S << "-0.000000e+00";
+      const char *NegativeZero = "-0.000000e+00";
+      writePadding(S, Width, strlen(NegativeZero));
+      S << NegativeZero;
       return;
     }
 #else
@@ -148,16 +188,18 @@ void llvm::write_double(raw_ostream &S, double N, std::size_t MinWidth,
 
     // negative zero
     if (fpcl == _FPCLASS_NZ) {
-      S << "-0.000000e+00";
+      const char *NegativeZero = "-0.000000e+00";
+      S << NegativeZero;
       return;
     }
 #endif
 
-    char buf[16];
+    char buf[32];
     unsigned len;
     len = format(Spec.c_str(), N).snprint(buf, sizeof(buf));
     if (len <= sizeof(buf) - 2) {
-      if (len >= 5 && buf[len - 5] == 'e' && buf[len - 3] == '0') {
+      if (len >= 5 && (buf[len - 5] == 'e' || buf[len - 5] == 'E') &&
+          buf[len - 3] == '0') {
         int cs = buf[len - 4];
         if (cs == '+' || cs == '-') {
           int c1 = buf[len - 2];
@@ -177,5 +219,27 @@ void llvm::write_double(raw_ostream &S, double N, std::size_t MinWidth,
 #endif
   }
 
-  S << format(Spec.c_str(), N);
+  if (Style == FloatStyle::Percent)
+    N *= 100.0;
+
+  char Buf[32];
+  unsigned Len;
+  Len = format(Spec.c_str(), N).snprint(Buf, sizeof(Buf));
+  if (Style == FloatStyle::Percent)
+    ++Len;
+  S << Buf;
+  if (Style == FloatStyle::Percent)
+    S << '%';
+}
+
+size_t llvm::getDefaultPrecision(FloatStyle Style) {
+  switch (Style) {
+  case FloatStyle::Exponent:
+  case FloatStyle::ExponentUpper:
+    return 6; // Number of decimal places.
+  case FloatStyle::Fixed:
+  case FloatStyle::Percent:
+    return 2; // Number of decimal places.
+  }
+  LLVM_BUILTIN_UNREACHABLE;
 }
index 8101bc1..b6835e3 100644 (file)
@@ -114,27 +114,27 @@ void raw_ostream::SetBufferAndMode(char *BufferStart, size_t Size,
 }
 
 raw_ostream &raw_ostream::operator<<(unsigned long N) {
-  write_ulong(*this, N, 0);
+  write_integer(*this, static_cast<uint64_t>(N), IntegerStyle::Integer);
   return *this;
 }
 
 raw_ostream &raw_ostream::operator<<(long N) {
-  write_long(*this, N, 0);
+  write_integer(*this, static_cast<int64_t>(N), IntegerStyle::Integer);
   return *this;
 }
 
 raw_ostream &raw_ostream::operator<<(unsigned long long N) {
-  write_ulonglong(*this, N, 0);
+  write_integer(*this, static_cast<uint64_t>(N), IntegerStyle::Integer);
   return *this;
 }
 
 raw_ostream &raw_ostream::operator<<(long long N) {
-  write_longlong(*this, N, 0);
+  write_integer(*this, static_cast<int64_t>(N), IntegerStyle::Integer);
   return *this;
 }
 
 raw_ostream &raw_ostream::write_hex(unsigned long long N) {
-  llvm::write_hex(*this, N, 0, false, false);
+  llvm::write_hex(*this, N, HexPrintStyle::Lower);
   return *this;
 }
 
@@ -179,12 +179,12 @@ raw_ostream &raw_ostream::write_escaped(StringRef Str,
 }
 
 raw_ostream &raw_ostream::operator<<(const void *P) {
-  llvm::write_hex(*this, (uintptr_t)P, 0, false, true);
+  llvm::write_hex(*this, (uintptr_t)P, HexPrintStyle::PrefixLower);
   return *this;
 }
 
 raw_ostream &raw_ostream::operator<<(double N) {
-  llvm::write_double(*this, N, 0, 0, FloatStyle::Exponent);
+  llvm::write_double(*this, N, FloatStyle::Exponent);
   return *this;
 }
 
@@ -331,9 +331,23 @@ raw_ostream &raw_ostream::operator<<(const FormattedString &FS) {
 
 raw_ostream &raw_ostream::operator<<(const FormattedNumber &FN) {
   if (FN.Hex) {
-    llvm::write_hex(*this, FN.HexValue, FN.Width, FN.Upper, FN.HexPrefix);
+    HexPrintStyle Style;
+    if (FN.Upper && FN.HexPrefix)
+      Style = HexPrintStyle::PrefixUpper;
+    else if (FN.Upper && !FN.HexPrefix)
+      Style = HexPrintStyle::Upper;
+    else if (!FN.Upper && FN.HexPrefix)
+      Style = HexPrintStyle::PrefixLower;
+    else
+      Style = HexPrintStyle::Lower;
+    llvm::write_hex(*this, FN.HexValue, Style, FN.Width);
   } else {
-    llvm::write_longlong(*this, FN.DecValue, FN.Width);
+    llvm::SmallString<16> Buffer;
+    llvm::raw_svector_ostream Stream(Buffer);
+    llvm::write_integer(Stream, FN.DecValue, IntegerStyle::Integer);
+    if (Buffer.size() < FN.Width)
+      indent(FN.Width - Buffer.size());
+    (*this) << Buffer;
   }
   return *this;
 }
index 5ff5bdf..daf6524 100644 (file)
@@ -29,6 +29,7 @@ add_llvm_unittest(SupportTests
   MathExtrasTest.cpp
   MemoryBufferTest.cpp
   MemoryTest.cpp
+  NativeFormatTests.cpp
   Path.cpp
   ProcessTest.cpp
   ProgramTest.cpp
diff --git a/llvm/unittests/Support/NativeFormatTests.cpp b/llvm/unittests/Support/NativeFormatTests.cpp
new file mode 100644 (file)
index 0000000..94508b7
--- /dev/null
@@ -0,0 +1,176 @@
+//===- llvm/unittest/Support/NativeFormatTests.cpp - formatting tests -----===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/NativeFormatting.h"
+#include "llvm/Support/raw_ostream.h"
+#include "gtest/gtest.h"
+
+#include <type_traits>
+
+using namespace llvm;
+
+namespace {
+
+template <typename T> std::string format_number(T N, IntegerStyle Style) {
+  std::string S;
+  llvm::raw_string_ostream Str(S);
+  write_integer(Str, N, Style);
+  Str.flush();
+  return S;
+}
+
+std::string format_number(uint64_t N, HexPrintStyle Style,
+                          Optional<size_t> Width = None) {
+  std::string S;
+  llvm::raw_string_ostream Str(S);
+  write_hex(Str, N, Style, Width);
+  Str.flush();
+  return S;
+}
+
+std::string format_number(double D, FloatStyle Style,
+                          Optional<size_t> Precision = None) {
+  std::string S;
+  llvm::raw_string_ostream Str(S);
+  write_double(Str, D, Style, Precision);
+  Str.flush();
+  return S;
+}
+
+// Test basic number formatting with various styles and default width and
+// precision.
+TEST(NativeFormatTest, BasicIntegerTests) {
+  // Simple integers with no decimal.
+  EXPECT_EQ("0", format_number(0, IntegerStyle::Integer));
+  EXPECT_EQ("2425", format_number(2425, IntegerStyle::Integer));
+  EXPECT_EQ("-2425", format_number(-2425, IntegerStyle::Integer));
+
+  EXPECT_EQ("0", format_number(0LL, IntegerStyle::Integer));
+  EXPECT_EQ("257257257235709",
+            format_number(257257257235709LL, IntegerStyle::Integer));
+  EXPECT_EQ("-257257257235709",
+            format_number(-257257257235709LL, IntegerStyle::Integer));
+
+  // Number formatting.
+  EXPECT_EQ("0", format_number(0, IntegerStyle::Number));
+  EXPECT_EQ("2,425", format_number(2425, IntegerStyle::Number));
+  EXPECT_EQ("-2,425", format_number(-2425, IntegerStyle::Number));
+  EXPECT_EQ("257,257,257,235,709",
+            format_number(257257257235709LL, IntegerStyle::Number));
+  EXPECT_EQ("-257,257,257,235,709",
+            format_number(-257257257235709LL, IntegerStyle::Number));
+
+  // Hex formatting.
+  // lower case, prefix.
+  EXPECT_EQ("0x0", format_number(0, HexPrintStyle::PrefixLower));
+  EXPECT_EQ("0xbeef", format_number(0xbeefLL, HexPrintStyle::PrefixLower));
+  EXPECT_EQ("0xdeadbeef",
+            format_number(0xdeadbeefLL, HexPrintStyle::PrefixLower));
+
+  // upper-case, prefix.
+  EXPECT_EQ("0x0", format_number(0, HexPrintStyle::PrefixUpper));
+  EXPECT_EQ("0xBEEF", format_number(0xbeefLL, HexPrintStyle::PrefixUpper));
+  EXPECT_EQ("0xDEADBEEF",
+            format_number(0xdeadbeefLL, HexPrintStyle::PrefixUpper));
+
+  // lower-case, no prefix
+  EXPECT_EQ("0", format_number(0, HexPrintStyle::Lower));
+  EXPECT_EQ("beef", format_number(0xbeefLL, HexPrintStyle::Lower));
+  EXPECT_EQ("deadbeef", format_number(0xdeadbeefLL, HexPrintStyle::Lower));
+
+  // upper-case, no prefix.
+  EXPECT_EQ("0", format_number(0, HexPrintStyle::Upper));
+  EXPECT_EQ("BEEF", format_number(0xbeef, HexPrintStyle::Upper));
+  EXPECT_EQ("DEADBEEF", format_number(0xdeadbeef, HexPrintStyle::Upper));
+}
+
+// Test basic floating point formatting with various styles and default width
+// and precision.
+TEST(NativeFormatTest, BasicFloatingPointTests) {
+  // Double
+  EXPECT_EQ("0.000000e+00", format_number(0.0, FloatStyle::Exponent));
+  EXPECT_EQ("-0.000000e+00", format_number(-0.0, FloatStyle::Exponent));
+  EXPECT_EQ("1.100000e+00", format_number(1.1, FloatStyle::Exponent));
+  EXPECT_EQ("1.100000E+00", format_number(1.1, FloatStyle::ExponentUpper));
+
+  // Default precision is 2 for floating points.
+  EXPECT_EQ("1.10", format_number(1.1, FloatStyle::Fixed));
+  EXPECT_EQ("1.34", format_number(1.34, FloatStyle::Fixed));
+  EXPECT_EQ("1.34", format_number(1.344, FloatStyle::Fixed));
+  EXPECT_EQ("1.35", format_number(1.346, FloatStyle::Fixed));
+}
+
+// Test common boundary cases and min/max conditions.
+TEST(NativeFormatTest, BoundaryTests) {
+  // Min and max.
+  EXPECT_EQ("18446744073709551615",
+            format_number(UINT64_MAX, IntegerStyle::Integer));
+
+  EXPECT_EQ("9223372036854775807",
+            format_number(INT64_MAX, IntegerStyle::Integer));
+  EXPECT_EQ("-9223372036854775808",
+            format_number(INT64_MIN, IntegerStyle::Integer));
+
+  EXPECT_EQ("4294967295", format_number(UINT32_MAX, IntegerStyle::Integer));
+  EXPECT_EQ("2147483647", format_number(INT32_MAX, IntegerStyle::Integer));
+  EXPECT_EQ("-2147483648", format_number(INT32_MIN, IntegerStyle::Integer));
+
+  EXPECT_EQ("nan", format_number(std::numeric_limits<double>::quiet_NaN(),
+                                 FloatStyle::Fixed));
+  EXPECT_EQ("INF", format_number(std::numeric_limits<double>::infinity(),
+                                 FloatStyle::Fixed));
+}
+
+TEST(NativeFormatTest, HexTests) {
+  // Test hex formatting with different widths and precisions.
+
+  // Width less than the value should print the full value anyway.
+  EXPECT_EQ("0x0", format_number(0, HexPrintStyle::PrefixLower, 0));
+  EXPECT_EQ("0xabcde", format_number(0xABCDE, HexPrintStyle::PrefixLower, 3));
+
+  // Precision greater than the value should pad with 0s.
+  // TODO: The prefix should not be counted in the precision.  But unfortunately
+  // it is and we have to live with it unless we fix all existing users of
+  // prefixed hex formatting.
+  EXPECT_EQ("0x000", format_number(0, HexPrintStyle::PrefixLower, 5));
+  EXPECT_EQ("0x0abcde", format_number(0xABCDE, HexPrintStyle::PrefixLower, 8));
+
+  EXPECT_EQ("00000", format_number(0, HexPrintStyle::Lower, 5));
+  EXPECT_EQ("000abcde", format_number(0xABCDE, HexPrintStyle::Lower, 8));
+
+  // Try printing more digits than can fit in a uint64.
+  EXPECT_EQ("0x00000000000000abcde",
+            format_number(0xABCDE, HexPrintStyle::PrefixLower, 21));
+}
+
+TEST(NativeFormatTest, IntegerTests) {
+  EXPECT_EQ("-10", format_number(-10, IntegerStyle::Integer));
+  EXPECT_EQ("-100", format_number(-100, IntegerStyle::Integer));
+  EXPECT_EQ("-1000", format_number(-1000, IntegerStyle::Integer));
+  EXPECT_EQ("-1234567890", format_number(-1234567890, IntegerStyle::Integer));
+  EXPECT_EQ("10", format_number(10, IntegerStyle::Integer));
+  EXPECT_EQ("100", format_number(100, IntegerStyle::Integer));
+  EXPECT_EQ("1000", format_number(1000, IntegerStyle::Integer));
+  EXPECT_EQ("1234567890", format_number(1234567890, IntegerStyle::Integer));
+}
+
+TEST(NativeFormatTest, CommaTests) {
+  EXPECT_EQ("0", format_number(0, IntegerStyle::Number));
+  EXPECT_EQ("10", format_number(10, IntegerStyle::Number));
+  EXPECT_EQ("100", format_number(100, IntegerStyle::Number));
+  EXPECT_EQ("1,000", format_number(1000, IntegerStyle::Number));
+  EXPECT_EQ("1,234,567,890", format_number(1234567890, IntegerStyle::Number));
+
+  EXPECT_EQ("-10", format_number(-10, IntegerStyle::Number));
+  EXPECT_EQ("-100", format_number(-100, IntegerStyle::Number));
+  EXPECT_EQ("-1,000", format_number(-1000, IntegerStyle::Number));
+  EXPECT_EQ("-1,234,567,890", format_number(-1234567890, IntegerStyle::Number));
+}
+}