Add setting precision for json writers and also add decimal places precision type. (#752)

* Added setting precision for writers.
* Added special case for precise precision and global precision.
* Added good setting of type of precision and also added this type to BuiltStreamWriter and for its settings.
* Added some tests.
This commit is contained in:
Mike R 2018-03-13 23:35:31 +03:00 committed by Christopher Dunn
parent af2598cdd3
commit a07fc53287
6 changed files with 85 additions and 8 deletions

View File

@ -109,6 +109,13 @@ enum CommentPlacement {
numberOfCommentPlacement numberOfCommentPlacement
}; };
/** \brief Type of precision for formatting of real values.
*/
enum PrecisionType {
significantDigits = 0, ///< we set max number of significant digits in string
decimalPlaces ///< we set max number of digits after "." in string
};
//# ifdef JSON_USE_CPPTL //# ifdef JSON_USE_CPPTL
// typedef CppTL::AnyEnumerator<const char *> EnumMemberNames; // typedef CppTL::AnyEnumerator<const char *> EnumMemberNames;
// typedef CppTL::AnyEnumerator<const Value &> EnumValues; // typedef CppTL::AnyEnumerator<const Value &> EnumValues;
@ -220,6 +227,9 @@ public:
static const UInt64 maxUInt64; static const UInt64 maxUInt64;
#endif // defined(JSON_HAS_INT64) #endif // defined(JSON_HAS_INT64)
/// Default precision for real value for string representation.
static const UInt defaultRealPrecision;
// Workaround for bug in the NVIDIAs CUDA 9.1 nvcc compiler // Workaround for bug in the NVIDIAs CUDA 9.1 nvcc compiler
// when using gcc and clang backend compilers. CZString // when using gcc and clang backend compilers. CZString
// cannot be defined as private. See issue #486 // cannot be defined as private. See issue #486

View File

@ -106,6 +106,10 @@ public:
- If true, outputs non-finite floating point values in the following way: - If true, outputs non-finite floating point values in the following way:
NaN values as "NaN", positive infinity as "Infinity", and negative infinity NaN values as "NaN", positive infinity as "Infinity", and negative infinity
as "-Infinity". as "-Infinity".
- "precision": int
- Number of precision digits for formatting of real values.
- "precisionType": "significant"(default) or "decimal"
- Type of precision for formatting of real values.
You can examine 'settings_` yourself You can examine 'settings_` yourself
to see the defaults. You can also write and read them just like any to see the defaults. You can also write and read them just like any
@ -339,7 +343,8 @@ JSONCPP_STRING JSON_API valueToString(UInt value);
#endif // if defined(JSON_HAS_INT64) #endif // if defined(JSON_HAS_INT64)
JSONCPP_STRING JSON_API valueToString(LargestInt value); JSONCPP_STRING JSON_API valueToString(LargestInt value);
JSONCPP_STRING JSON_API valueToString(LargestUInt value); JSONCPP_STRING JSON_API valueToString(LargestUInt value);
JSONCPP_STRING JSON_API valueToString(double value); JSONCPP_STRING JSON_API valueToString(double value, unsigned int precision = Value::defaultRealPrecision,
PrecisionType precisionType = PrecisionType::significantDigits);
JSONCPP_STRING JSON_API valueToString(bool value); JSONCPP_STRING JSON_API valueToString(bool value);
JSONCPP_STRING JSON_API valueToQuotedString(const char* value); JSONCPP_STRING JSON_API valueToQuotedString(const char* value);

View File

@ -109,6 +109,20 @@ static inline void fixNumericLocaleInput(char* begin, char* end) {
} }
} }
/**
* Delete zeros in the end of string, if it isn't last zero before '.' character.
*/
static inline void fixZerosInTheEnd(char* begin, char* end) {
end--;
while ((begin < end) && (*end == '0')) {
// don't delete last zero before point.
if (*(end - 1) != '.') {
*end = '\0';
}
end--;
}
}
} // namespace Json { } // namespace Json {
#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED #endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED

View File

@ -64,6 +64,8 @@ const LargestInt Value::minLargestInt = LargestInt(~(LargestUInt(-1) / 2));
const LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2); const LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2);
const LargestUInt Value::maxLargestUInt = LargestUInt(-1); const LargestUInt Value::maxLargestUInt = LargestUInt(-1);
const UInt Value::defaultRealPrecision = 17;
#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) #if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
template <typename T, typename U> template <typename T, typename U>
static inline bool InRange(double d, T min, U max) { static inline bool InRange(double d, T min, U max) {

View File

@ -118,14 +118,18 @@ JSONCPP_STRING valueToString(UInt value) {
#endif // # if defined(JSON_HAS_INT64) #endif // # if defined(JSON_HAS_INT64)
namespace { namespace {
JSONCPP_STRING valueToString(double value, bool useSpecialFloats, unsigned int precision) { JSONCPP_STRING valueToString(double value, bool useSpecialFloats, unsigned int precision, PrecisionType precisionType) {
// Allocate a buffer that is more than large enough to store the 16 digits of // Allocate a buffer that is more than large enough to store the 16 digits of
// precision requested below. // precision requested below.
char buffer[36]; char buffer[36];
int len = -1; int len = -1;
char formatString[15]; char formatString[15];
snprintf(formatString, sizeof(formatString), "%%.%ug", precision); if (precisionType == PrecisionType::significantDigits) {
snprintf(formatString, sizeof(formatString), "%%.%ug", precision);
} else {
snprintf(formatString, sizeof(formatString), "%%.%uf", precision);
}
// Print into the buffer. We need not request the alternative representation // Print into the buffer. We need not request the alternative representation
// that always has a decimal point because JSON doesn't distinguish the // that always has a decimal point because JSON doesn't distinguish the
@ -133,6 +137,10 @@ JSONCPP_STRING valueToString(double value, bool useSpecialFloats, unsigned int p
if (isfinite(value)) { if (isfinite(value)) {
len = snprintf(buffer, sizeof(buffer), formatString, value); len = snprintf(buffer, sizeof(buffer), formatString, value);
fixNumericLocale(buffer, buffer + len); fixNumericLocale(buffer, buffer + len);
// to delete use-less too much zeros in the end of string
if (precisionType == PrecisionType::decimalPlaces) {
fixZerosInTheEnd(buffer, buffer + len);
}
// try to ensure we preserve the fact that this was given to us as a double on input // try to ensure we preserve the fact that this was given to us as a double on input
if (!strchr(buffer, '.') && !strchr(buffer, 'e')) { if (!strchr(buffer, '.') && !strchr(buffer, 'e')) {
@ -154,7 +162,9 @@ JSONCPP_STRING valueToString(double value, bool useSpecialFloats, unsigned int p
} }
} }
JSONCPP_STRING valueToString(double value) { return valueToString(value, false, 17); } JSONCPP_STRING valueToString(double value, unsigned int precision, PrecisionType precisionType) {
return valueToString(value, false, precision, precisionType);
}
JSONCPP_STRING valueToString(bool value) { return value ? "true" : "false"; } JSONCPP_STRING valueToString(bool value) { return value ? "true" : "false"; }
@ -856,7 +866,8 @@ struct BuiltStyledStreamWriter : public StreamWriter
JSONCPP_STRING const& nullSymbol, JSONCPP_STRING const& nullSymbol,
JSONCPP_STRING const& endingLineFeedSymbol, JSONCPP_STRING const& endingLineFeedSymbol,
bool useSpecialFloats, bool useSpecialFloats,
unsigned int precision); unsigned int precision,
PrecisionType precisionType);
int write(Value const& root, JSONCPP_OSTREAM* sout) JSONCPP_OVERRIDE; int write(Value const& root, JSONCPP_OSTREAM* sout) JSONCPP_OVERRIDE;
private: private:
void writeValue(Value const& value); void writeValue(Value const& value);
@ -885,6 +896,7 @@ private:
bool indented_ : 1; bool indented_ : 1;
bool useSpecialFloats_ : 1; bool useSpecialFloats_ : 1;
unsigned int precision_; unsigned int precision_;
PrecisionType precisionType_;
}; };
BuiltStyledStreamWriter::BuiltStyledStreamWriter( BuiltStyledStreamWriter::BuiltStyledStreamWriter(
JSONCPP_STRING const& indentation, JSONCPP_STRING const& indentation,
@ -893,7 +905,8 @@ BuiltStyledStreamWriter::BuiltStyledStreamWriter(
JSONCPP_STRING const& nullSymbol, JSONCPP_STRING const& nullSymbol,
JSONCPP_STRING const& endingLineFeedSymbol, JSONCPP_STRING const& endingLineFeedSymbol,
bool useSpecialFloats, bool useSpecialFloats,
unsigned int precision) unsigned int precision,
PrecisionType precisionType)
: rightMargin_(74) : rightMargin_(74)
, indentation_(indentation) , indentation_(indentation)
, cs_(cs) , cs_(cs)
@ -904,6 +917,7 @@ BuiltStyledStreamWriter::BuiltStyledStreamWriter(
, indented_(false) , indented_(false)
, useSpecialFloats_(useSpecialFloats) , useSpecialFloats_(useSpecialFloats)
, precision_(precision) , precision_(precision)
, precisionType_(precisionType)
{ {
} }
int BuiltStyledStreamWriter::write(Value const& root, JSONCPP_OSTREAM* sout) int BuiltStyledStreamWriter::write(Value const& root, JSONCPP_OSTREAM* sout)
@ -933,7 +947,7 @@ void BuiltStyledStreamWriter::writeValue(Value const& value) {
pushValue(valueToString(value.asLargestUInt())); pushValue(valueToString(value.asLargestUInt()));
break; break;
case realValue: case realValue:
pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_)); pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_, precisionType_));
break; break;
case stringValue: case stringValue:
{ {
@ -1145,6 +1159,7 @@ StreamWriter* StreamWriterBuilder::newStreamWriter() const
{ {
JSONCPP_STRING indentation = settings_["indentation"].asString(); JSONCPP_STRING indentation = settings_["indentation"].asString();
JSONCPP_STRING cs_str = settings_["commentStyle"].asString(); JSONCPP_STRING cs_str = settings_["commentStyle"].asString();
JSONCPP_STRING pt_str = settings_["precisionType"].asString();
bool eyc = settings_["enableYAMLCompatibility"].asBool(); bool eyc = settings_["enableYAMLCompatibility"].asBool();
bool dnp = settings_["dropNullPlaceholders"].asBool(); bool dnp = settings_["dropNullPlaceholders"].asBool();
bool usf = settings_["useSpecialFloats"].asBool(); bool usf = settings_["useSpecialFloats"].asBool();
@ -1157,6 +1172,14 @@ StreamWriter* StreamWriterBuilder::newStreamWriter() const
} else { } else {
throwRuntimeError("commentStyle must be 'All' or 'None'"); throwRuntimeError("commentStyle must be 'All' or 'None'");
} }
PrecisionType precisionType(significantDigits);
if (pt_str == "significant") {
precisionType = PrecisionType::significantDigits;
} else if (pt_str == "decimal") {
precisionType = PrecisionType::decimalPlaces;
} else {
throwRuntimeError("precisionType must be 'significant' or 'decimal'");
}
JSONCPP_STRING colonSymbol = " : "; JSONCPP_STRING colonSymbol = " : ";
if (eyc) { if (eyc) {
colonSymbol = ": "; colonSymbol = ": ";
@ -1171,7 +1194,7 @@ StreamWriter* StreamWriterBuilder::newStreamWriter() const
JSONCPP_STRING endingLineFeedSymbol; JSONCPP_STRING endingLineFeedSymbol;
return new BuiltStyledStreamWriter( return new BuiltStyledStreamWriter(
indentation, cs, indentation, cs,
colonSymbol, nullSymbol, endingLineFeedSymbol, usf, pre); colonSymbol, nullSymbol, endingLineFeedSymbol, usf, pre, precisionType);
} }
static void getValidWriterKeys(std::set<JSONCPP_STRING>* valid_keys) static void getValidWriterKeys(std::set<JSONCPP_STRING>* valid_keys)
{ {
@ -1182,6 +1205,7 @@ static void getValidWriterKeys(std::set<JSONCPP_STRING>* valid_keys)
valid_keys->insert("dropNullPlaceholders"); valid_keys->insert("dropNullPlaceholders");
valid_keys->insert("useSpecialFloats"); valid_keys->insert("useSpecialFloats");
valid_keys->insert("precision"); valid_keys->insert("precision");
valid_keys->insert("precisionType");
} }
bool StreamWriterBuilder::validate(Json::Value* invalid) const bool StreamWriterBuilder::validate(Json::Value* invalid) const
{ {
@ -1214,6 +1238,7 @@ void StreamWriterBuilder::setDefaults(Json::Value* settings)
(*settings)["dropNullPlaceholders"] = false; (*settings)["dropNullPlaceholders"] = false;
(*settings)["useSpecialFloats"] = false; (*settings)["useSpecialFloats"] = false;
(*settings)["precision"] = 17; (*settings)["precision"] = 17;
(*settings)["precisionType"] = "significant";
//! [StreamWriterBuilderDefaults] //! [StreamWriterBuilderDefaults]
} }

View File

@ -1757,6 +1757,27 @@ JSONTEST_FIXTURE(ValueTest, precision) {
expected = "0.25634569487374054"; expected = "0.25634569487374054";
result = Json::writeString(b, v); result = Json::writeString(b, v);
JSONTEST_ASSERT_STRING_EQUAL(expected, result); JSONTEST_ASSERT_STRING_EQUAL(expected, result);
b.settings_["precision"] = 5;
b.settings_["precisionType"] = "decimal";
v = 0.256345694873740545068;
expected = "0.25635";
result = Json::writeString(b, v);
JSONTEST_ASSERT_STRING_EQUAL(expected, result);
b.settings_["precision"] = 1;
b.settings_["precisionType"] = "decimal";
v = 0.256345694873740545068;
expected = "0.3";
result = Json::writeString(b, v);
JSONTEST_ASSERT_STRING_EQUAL(expected, result);
b.settings_["precision"] = 10;
b.settings_["precisionType"] = "decimal";
v = 0.23300000;
expected = "0.233";
result = Json::writeString(b, v);
JSONTEST_ASSERT_STRING_EQUAL(expected, result);
} }
struct WriterTest : JsonTest::TestCase {}; struct WriterTest : JsonTest::TestCase {};