Skip to content

Commit

Permalink
Replaced the complex implementation of valueToString(double).
Browse files Browse the repository at this point in the history
The previous one was confusing and prone to buffer overflows, and didn't
work correctly with 16-decimal-digit numbers. The new one simply uses
snprintf with a standard format string.

The major change is that we don't always print a decimal point now.
Fortunately, JSON doesn't distinguish between integers and reals.
  • Loading branch information
jacobsa committed Aug 8, 2013
1 parent bb53cd0 commit 32ffb93
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 44 deletions.
41 changes: 10 additions & 31 deletions src/lib_json/json_writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,40 +73,19 @@ std::string valueToString( UInt value )

std::string valueToString( double value )
{
// Allocate a buffer that is more than large enough to store the 16 digits of
// precision requested below.
char buffer[32];

// Print into the buffer. We need not request the alternative representation
// that always has a decimal point because JSON doesn't distingish the
// concepts of reals and integers.
#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning.
sprintf_s(buffer, sizeof(buffer), "%#.16g", value);
#else
snprintf(buffer, sizeof(buffer), "%#.16g", value);
sprintf_s(buffer, sizeof(buffer), "%.16g", value);
#else
snprintf(buffer, sizeof(buffer), "%.16g", value);
#endif
char* ch = buffer + strlen(buffer) - 1;
if (*ch != '0') return buffer; // nothing to truncate, so save time
while(ch > buffer && *ch == '0'){
--ch;
}
char* last_nonzero = ch;
while(ch >= buffer){
switch(*ch){
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
--ch;
continue;
case '.':
// Truncate zeroes to save bytes in output, but keep one.
*(last_nonzero+2) = '\0';
return buffer;
default:
return buffer;
}
}

return buffer;
}

Expand Down
26 changes: 13 additions & 13 deletions src/test_lib_json/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ JSONTEST_FIXTURE( ValueTest, integers )
JSONTEST_ASSERT_EQUAL(0.0, val.asDouble());
JSONTEST_ASSERT_EQUAL(0.0, val.asFloat());
JSONTEST_ASSERT_EQUAL(false, val.asBool());
JSONTEST_ASSERT_STRING_EQUAL("0.0", val.asString());
JSONTEST_ASSERT_STRING_EQUAL("0", val.asString());

// Zero (signed constructor arg)
val = Json::Value(0);
Expand Down Expand Up @@ -546,7 +546,7 @@ JSONTEST_FIXTURE( ValueTest, integers )
JSONTEST_ASSERT_EQUAL(0.0, val.asDouble());
JSONTEST_ASSERT_EQUAL(0.0, val.asFloat());
JSONTEST_ASSERT_EQUAL(false, val.asBool());
JSONTEST_ASSERT_STRING_EQUAL("0.0", val.asString());
JSONTEST_ASSERT_STRING_EQUAL("0", val.asString());

// 2^20 (signed constructor arg)
val = Json::Value(1 << 20);
Expand Down Expand Up @@ -629,7 +629,7 @@ JSONTEST_FIXTURE( ValueTest, integers )
JSONTEST_ASSERT_EQUAL((1 << 20), val.asDouble());
JSONTEST_ASSERT_EQUAL((1 << 20), val.asFloat());
JSONTEST_ASSERT_EQUAL(true, val.asBool());
JSONTEST_ASSERT_STRING_EQUAL("1048576.0", normalizeFloatingPointStr(val.asString()));
JSONTEST_ASSERT_STRING_EQUAL("1048576", normalizeFloatingPointStr(val.asString()));

// -2^20
val = Json::Value(-(1 << 20));
Expand Down Expand Up @@ -869,7 +869,7 @@ JSONTEST_FIXTURE( ValueTest, integers )
JSONTEST_ASSERT_EQUAL((Json::Int64(1) << 40), val.asDouble());
JSONTEST_ASSERT_EQUAL((Json::Int64(1) << 40), val.asFloat());
JSONTEST_ASSERT_EQUAL(true, val.asBool());
JSONTEST_ASSERT_STRING_EQUAL("1099511627776.0", normalizeFloatingPointStr(val.asString()));
JSONTEST_ASSERT_STRING_EQUAL("1099511627776", normalizeFloatingPointStr(val.asString()));

// -2^40
val = Json::Value(-(Json::Int64(1) << 40));
Expand Down Expand Up @@ -1035,7 +1035,7 @@ JSONTEST_FIXTURE( ValueTest, integers )
JSONTEST_ASSERT_EQUAL(1e19, val.asDouble());
JSONTEST_ASSERT_EQUAL(1e19, val.asFloat());
JSONTEST_ASSERT_EQUAL(true, val.asBool());
JSONTEST_ASSERT_STRING_EQUAL("1.000000000000000e+19", normalizeFloatingPointStr(val.asString()));
JSONTEST_ASSERT_STRING_EQUAL("1e+19", normalizeFloatingPointStr(val.asString()));

// uint64 max
val = Json::Value(Json::UInt64(kuint64max));
Expand Down Expand Up @@ -1114,7 +1114,7 @@ JSONTEST_FIXTURE( ValueTest, nonIntegers )
JSONTEST_ASSERT_EQUAL(1, val.asUInt());
JSONTEST_ASSERT_EQUAL(1, val.asLargestUInt());
JSONTEST_ASSERT_EQUAL(true, val.asBool());
JSONTEST_ASSERT_EQUAL("1.50", val.asString());
JSONTEST_ASSERT_EQUAL("1.5", val.asString());

// Small negative number
val = Json::Value(-1.5);
Expand All @@ -1140,7 +1140,7 @@ JSONTEST_FIXTURE( ValueTest, nonIntegers )
JSONTEST_ASSERT_EQUAL(-1, val.asInt());
JSONTEST_ASSERT_EQUAL(-1, val.asLargestInt());
JSONTEST_ASSERT_EQUAL(true, val.asBool());
JSONTEST_ASSERT_EQUAL("-1.50", val.asString());
JSONTEST_ASSERT_EQUAL("-1.5", val.asString());

// A bit over int32 max
val = Json::Value(kint32max + 0.5);
Expand Down Expand Up @@ -1169,7 +1169,7 @@ JSONTEST_FIXTURE( ValueTest, nonIntegers )
JSONTEST_ASSERT_EQUAL(2147483647U, val.asLargestUInt());
#endif
JSONTEST_ASSERT_EQUAL(true, val.asBool());
JSONTEST_ASSERT_EQUAL("2147483647.50", normalizeFloatingPointStr(val.asString()));
JSONTEST_ASSERT_EQUAL("2147483647.5", normalizeFloatingPointStr(val.asString()));

// A bit under int32 min
val = Json::Value(kint32min - 0.5);
Expand All @@ -1196,7 +1196,7 @@ JSONTEST_FIXTURE( ValueTest, nonIntegers )
JSONTEST_ASSERT_EQUAL(-Json::Int64(1)<< 31, val.asLargestInt());
#endif
JSONTEST_ASSERT_EQUAL(true, val.asBool());
JSONTEST_ASSERT_EQUAL("-2147483648.50", normalizeFloatingPointStr(val.asString()));
JSONTEST_ASSERT_EQUAL("-2147483648.5", normalizeFloatingPointStr(val.asString()));

// A bit over uint32 max
val = Json::Value(kuint32max + 0.5);
Expand Down Expand Up @@ -1224,15 +1224,15 @@ JSONTEST_FIXTURE( ValueTest, nonIntegers )
JSONTEST_ASSERT_EQUAL((Json::UInt64(1) << 32)-Json::UInt64(1), val.asLargestUInt());
#endif
JSONTEST_ASSERT_EQUAL(true, val.asBool());
JSONTEST_ASSERT_EQUAL("4294967295.50", normalizeFloatingPointStr(val.asString()));
JSONTEST_ASSERT_EQUAL("4294967295.5", normalizeFloatingPointStr(val.asString()));

val = Json::Value(1.2345678901234);
JSONTEST_ASSERT_STRING_EQUAL( "1.23456789012340", normalizeFloatingPointStr(val.asString()));
JSONTEST_ASSERT_STRING_EQUAL( "1.2345678901234", normalizeFloatingPointStr(val.asString()));

// A 16-digit floating point number.
val = Json::Value(2199023255552000.0f);
JSONTEST_ASSERT_EQUAL(float(2199023255552000), val.asFloat());
JSONTEST_ASSERT_STRING_EQUAL("2199023255552000.", normalizeFloatingPointStr(val.asString()));
JSONTEST_ASSERT_STRING_EQUAL("2199023255552000", normalizeFloatingPointStr(val.asString()));

// A very large floating point number.
val = Json::Value(3.402823466385289e38);
Expand All @@ -1242,7 +1242,7 @@ JSONTEST_FIXTURE( ValueTest, nonIntegers )
// An even larger floating point number.
val = Json::Value(1.2345678e300);
JSONTEST_ASSERT_EQUAL(double(1.2345678e300), val.asDouble());
JSONTEST_ASSERT_STRING_EQUAL("1.234567800000000e+300", normalizeFloatingPointStr(val.asString()));
JSONTEST_ASSERT_STRING_EQUAL("1.2345678e+300", normalizeFloatingPointStr(val.asString()));
}


Expand Down

0 comments on commit 32ffb93

Please sign in to comment.