diff --git a/format.cc b/format.cc index 52dbe79c..d27e132d 100644 --- a/format.cc +++ b/format.cc @@ -33,6 +33,7 @@ #include "format.h" +#include #include #include @@ -280,17 +281,21 @@ template void Formatter::FormatDouble(T value, const FormatSpec &spec, int precision) { // Check type. char type = spec.type; + bool upper = false; switch (type) { case 0: type = 'g'; break; - case 'e': case 'E': case 'f': case 'g': case 'G': + case 'e': case 'f': case 'g': break; case 'F': #ifdef _MSC_VER // MSVC's printf doesn't support 'F'. type = 'f'; #endif + // Fall through. + case 'E': case 'G': + upper = true; break; default: ReportUnknownType(type, "double"); @@ -298,12 +303,30 @@ void Formatter::FormatDouble(T value, const FormatSpec &spec, int precision) { } char sign = 0; - if (value < 0) { + // Use signbit instead of value < 0 because the latter is always + // false for NaN. + if (signbit(value)) { sign = '-'; value = -value; } else if ((spec.flags & SIGN_FLAG) != 0) { sign = (spec.flags & PLUS_FLAG) != 0 ? '+' : ' '; } + + if (isnan(value)) { + // Format NaN ourselves because sprintf's output is not consistent + // across platforms. + std::size_t size = 4; + const char *nan = upper ? " NAN" : " nan"; + if (!sign) { + --size; + ++nan; + } + char *out = FormatString(nan, size, spec); + if (sign) + *out = sign; + return; + } + size_t offset = buffer_.size(); unsigned width = spec.width; if (sign) { @@ -382,7 +405,7 @@ void Formatter::FormatDouble(T value, const FormatSpec &spec, int precision) { } } -void Formatter::FormatString( +char *Formatter::FormatString( const char *s, std::size_t size, const FormatSpec &spec) { char *out = 0; if (spec.width > size) { @@ -399,6 +422,7 @@ void Formatter::FormatString( out = GrowBuffer(size); } std::copy(s, s + size, out); + return out; } // Parses an unsigned integer advancing s to the end of the parsed input. diff --git a/format.h b/format.h index f7b82567..8605a7c2 100644 --- a/format.h +++ b/format.h @@ -322,7 +322,7 @@ class Formatter : public BasicFormatter { template void FormatDouble(T value, const FormatSpec &spec, int precision); - void FormatString(const char *s, std::size_t size, const FormatSpec &spec); + char *FormatString(const char *s, std::size_t size, const FormatSpec &spec); // Formats an argument of a custom type, such as a user-defined class. template diff --git a/format_test.cc b/format_test.cc index f6c54053..cde7455e 100644 --- a/format_test.cc +++ b/format_test.cc @@ -774,16 +774,24 @@ TEST(FormatterTest, FormatDouble) { sprintf(buffer, "%E", 392.65); EXPECT_EQ(buffer, str(Format("{0:E}") << 392.65)); EXPECT_EQ("+0000392.6", str(Format("{0:+010.4g}") << 392.65)); - double nan = std::numeric_limits::quiet_NaN(); - EXPECT_EQ("nan", str(Format("{}") << nan)); - EXPECT_EQ("-nan", str(Format("{}") << -nan)); - EXPECT_EQ("NAN", str(Format("{:F}") << nan)); double inf = std::numeric_limits::infinity(); EXPECT_EQ("inf", str(Format("{}") << inf)); EXPECT_EQ("-inf", str(Format("{}") << -inf)); EXPECT_EQ("INF", str(Format("{:F}") << inf)); } +TEST(FormatterTest, FormatNaN) { + double nan = std::numeric_limits::quiet_NaN(); + EXPECT_EQ("nan", str(Format("{}") << nan)); + EXPECT_EQ("+nan", str(Format("{:+}") << nan)); + EXPECT_EQ("-nan", str(Format("{}") << -nan)); + EXPECT_EQ(" nan", str(Format("{: }") << nan)); + EXPECT_EQ("NAN", str(Format("{:F}") << nan)); + EXPECT_EQ("nan ", str(Format("{:<7}") << nan)); + EXPECT_EQ(" nan ", str(Format("{:^7}") << nan)); + EXPECT_EQ(" nan", str(Format("{:>7}") << nan)); +} + TEST(FormatterTest, FormatLongDouble) { EXPECT_EQ("0", str(Format("{0:}") << 0.0l)); EXPECT_EQ("0.000000", str(Format("{0:f}") << 0.0l));