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
};
/** \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
// typedef CppTL::AnyEnumerator<const char *> EnumMemberNames;
// typedef CppTL::AnyEnumerator<const Value &> EnumValues;
@ -220,6 +227,9 @@ public:
static const UInt64 maxUInt64;
#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
// when using gcc and clang backend compilers. CZString
// 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:
NaN values as "NaN", positive infinity as "Infinity", and negative 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
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)
JSONCPP_STRING JSON_API valueToString(LargestInt 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 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 {
#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 LargestUInt Value::maxLargestUInt = LargestUInt(-1);
const UInt Value::defaultRealPrecision = 17;
#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
template <typename T, typename U>
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)
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
// precision requested below.
char buffer[36];
int len = -1;
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
// 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)) {
len = snprintf(buffer, sizeof(buffer), formatString, value);
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
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"; }
@ -856,7 +866,8 @@ struct BuiltStyledStreamWriter : public StreamWriter
JSONCPP_STRING const& nullSymbol,
JSONCPP_STRING const& endingLineFeedSymbol,
bool useSpecialFloats,
unsigned int precision);
unsigned int precision,
PrecisionType precisionType);
int write(Value const& root, JSONCPP_OSTREAM* sout) JSONCPP_OVERRIDE;
private:
void writeValue(Value const& value);
@ -885,6 +896,7 @@ private:
bool indented_ : 1;
bool useSpecialFloats_ : 1;
unsigned int precision_;
PrecisionType precisionType_;
};
BuiltStyledStreamWriter::BuiltStyledStreamWriter(
JSONCPP_STRING const& indentation,
@ -893,7 +905,8 @@ BuiltStyledStreamWriter::BuiltStyledStreamWriter(
JSONCPP_STRING const& nullSymbol,
JSONCPP_STRING const& endingLineFeedSymbol,
bool useSpecialFloats,
unsigned int precision)
unsigned int precision,
PrecisionType precisionType)
: rightMargin_(74)
, indentation_(indentation)
, cs_(cs)
@ -904,6 +917,7 @@ BuiltStyledStreamWriter::BuiltStyledStreamWriter(
, indented_(false)
, useSpecialFloats_(useSpecialFloats)
, precision_(precision)
, precisionType_(precisionType)
{
}
int BuiltStyledStreamWriter::write(Value const& root, JSONCPP_OSTREAM* sout)
@ -933,7 +947,7 @@ void BuiltStyledStreamWriter::writeValue(Value const& value) {
pushValue(valueToString(value.asLargestUInt()));
break;
case realValue:
pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_));
pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_, precisionType_));
break;
case stringValue:
{
@ -1145,6 +1159,7 @@ StreamWriter* StreamWriterBuilder::newStreamWriter() const
{
JSONCPP_STRING indentation = settings_["indentation"].asString();
JSONCPP_STRING cs_str = settings_["commentStyle"].asString();
JSONCPP_STRING pt_str = settings_["precisionType"].asString();
bool eyc = settings_["enableYAMLCompatibility"].asBool();
bool dnp = settings_["dropNullPlaceholders"].asBool();
bool usf = settings_["useSpecialFloats"].asBool();
@ -1157,6 +1172,14 @@ StreamWriter* StreamWriterBuilder::newStreamWriter() const
} else {
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 = " : ";
if (eyc) {
colonSymbol = ": ";
@ -1171,7 +1194,7 @@ StreamWriter* StreamWriterBuilder::newStreamWriter() const
JSONCPP_STRING endingLineFeedSymbol;
return new BuiltStyledStreamWriter(
indentation, cs,
colonSymbol, nullSymbol, endingLineFeedSymbol, usf, pre);
colonSymbol, nullSymbol, endingLineFeedSymbol, usf, pre, precisionType);
}
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("useSpecialFloats");
valid_keys->insert("precision");
valid_keys->insert("precisionType");
}
bool StreamWriterBuilder::validate(Json::Value* invalid) const
{
@ -1214,6 +1238,7 @@ void StreamWriterBuilder::setDefaults(Json::Value* settings)
(*settings)["dropNullPlaceholders"] = false;
(*settings)["useSpecialFloats"] = false;
(*settings)["precision"] = 17;
(*settings)["precisionType"] = "significant";
//! [StreamWriterBuilderDefaults]
}

View File

@ -1757,6 +1757,27 @@ JSONTEST_FIXTURE(ValueTest, precision) {
expected = "0.25634569487374054";
result = Json::writeString(b, v);
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 {};