Commit adb3974e authored by Milo Yip's avatar Milo Yip

Merge pull request #97 from miloyip/issue72customdtoa

Fix #72
parents 34dd0fd6 c5491529
...@@ -225,7 +225,7 @@ You may doubt that, why not just using `sprintf()` or `std::stringstream` to bui ...@@ -225,7 +225,7 @@ You may doubt that, why not just using `sprintf()` or `std::stringstream` to bui
There are various reasons: There are various reasons:
1. `Writer` must output a well-formed JSON. If there is incorrect event sequence (e.g. `Int()` just after `StartObject()`), it generates assertion fail in debug mode. 1. `Writer` must output a well-formed JSON. If there is incorrect event sequence (e.g. `Int()` just after `StartObject()`), it generates assertion fail in debug mode.
2. `Writer::String()` can handle string escaping (e.g. converting code point `U+000A` to `\n`) and Unicode transcoding. 2. `Writer::String()` can handle string escaping (e.g. converting code point `U+000A` to `\n`) and Unicode transcoding.
3. `Writer` handles number output consistently. For example, user can set precision for `Double()`. 3. `Writer` handles number output consistently.
4. `Writer` implements the event handler concept. It can be used to handle events from `Reader`, `Document` or other event publisher. 4. `Writer` implements the event handler concept. It can be used to handle events from `Reader`, `Document` or other event publisher.
5. `Writer` can be optimized for different platforms. 5. `Writer` can be optimized for different platforms.
...@@ -258,20 +258,6 @@ The last one, `Allocator` is the type of allocator, which is used for allocating ...@@ -258,20 +258,6 @@ The last one, `Allocator` is the type of allocator, which is used for allocating
Besides, the constructor of `Writer` has a `levelDepth` parameter. This parameter affects the initial memory allocated for storing information per hierarchy level. Besides, the constructor of `Writer` has a `levelDepth` parameter. This parameter affects the initial memory allocated for storing information per hierarchy level.
## Precision (#WriterPrecision)
When using `Double()`, the precision of output can be specified, for example:
~~~~~~~~~~cpp
writer.SetDoublePrecision(4);
writer.StartArary();
writer.Double(3.14159265359);
writer.EndArray();
~~~~~~~~~~
~~~~~~~~~~
[3.1416]
~~~~~~~~~~
## PrettyWriter {#PrettyWriter} ## PrettyWriter {#PrettyWriter}
While the output of `Writer` is the most condensed JSON without white-spaces, suitable for network transfer or storage, it is not easily readable by human. While the output of `Writer` is the most condensed JSON without white-spaces, suitable for network transfer or storage, it is not easily readable by human.
......
This diff is collapsed.
...@@ -6,27 +6,12 @@ namespace internal { ...@@ -6,27 +6,12 @@ namespace internal {
//! Computes integer powers of 10 in double (10.0^n). //! Computes integer powers of 10 in double (10.0^n).
/*! This function uses lookup table for fast and accurate results. /*! This function uses lookup table for fast and accurate results.
\param n positive/negative exponent. Must <= 308. \param n non-negative exponent. Must <= 308.
\return 10.0^n \return 10.0^n
*/ */
inline double Pow10(int n) { inline double Pow10(int n) {
static const double e[] = { // 1e-308...1e308: 617 * 8 bytes = 4936 bytes static const double e[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes
1e-308,1e-307,1e-306,1e-305,1e-304,1e-303,1e-302,1e-301,1e-300, 1e+0,
1e-299,1e-298,1e-297,1e-296,1e-295,1e-294,1e-293,1e-292,1e-291,1e-290,1e-289,1e-288,1e-287,1e-286,1e-285,1e-284,1e-283,1e-282,1e-281,1e-280,
1e-279,1e-278,1e-277,1e-276,1e-275,1e-274,1e-273,1e-272,1e-271,1e-270,1e-269,1e-268,1e-267,1e-266,1e-265,1e-264,1e-263,1e-262,1e-261,1e-260,
1e-259,1e-258,1e-257,1e-256,1e-255,1e-254,1e-253,1e-252,1e-251,1e-250,1e-249,1e-248,1e-247,1e-246,1e-245,1e-244,1e-243,1e-242,1e-241,1e-240,
1e-239,1e-238,1e-237,1e-236,1e-235,1e-234,1e-233,1e-232,1e-231,1e-230,1e-229,1e-228,1e-227,1e-226,1e-225,1e-224,1e-223,1e-222,1e-221,1e-220,
1e-219,1e-218,1e-217,1e-216,1e-215,1e-214,1e-213,1e-212,1e-211,1e-210,1e-209,1e-208,1e-207,1e-206,1e-205,1e-204,1e-203,1e-202,1e-201,1e-200,
1e-199,1e-198,1e-197,1e-196,1e-195,1e-194,1e-193,1e-192,1e-191,1e-190,1e-189,1e-188,1e-187,1e-186,1e-185,1e-184,1e-183,1e-182,1e-181,1e-180,
1e-179,1e-178,1e-177,1e-176,1e-175,1e-174,1e-173,1e-172,1e-171,1e-170,1e-169,1e-168,1e-167,1e-166,1e-165,1e-164,1e-163,1e-162,1e-161,1e-160,
1e-159,1e-158,1e-157,1e-156,1e-155,1e-154,1e-153,1e-152,1e-151,1e-150,1e-149,1e-148,1e-147,1e-146,1e-145,1e-144,1e-143,1e-142,1e-141,1e-140,
1e-139,1e-138,1e-137,1e-136,1e-135,1e-134,1e-133,1e-132,1e-131,1e-130,1e-129,1e-128,1e-127,1e-126,1e-125,1e-124,1e-123,1e-122,1e-121,1e-120,
1e-119,1e-118,1e-117,1e-116,1e-115,1e-114,1e-113,1e-112,1e-111,1e-110,1e-109,1e-108,1e-107,1e-106,1e-105,1e-104,1e-103,1e-102,1e-101,1e-100,
1e-99, 1e-98, 1e-97, 1e-96, 1e-95, 1e-94, 1e-93, 1e-92, 1e-91, 1e-90, 1e-89, 1e-88, 1e-87, 1e-86, 1e-85, 1e-84, 1e-83, 1e-82, 1e-81, 1e-80,
1e-79, 1e-78, 1e-77, 1e-76, 1e-75, 1e-74, 1e-73, 1e-72, 1e-71, 1e-70, 1e-69, 1e-68, 1e-67, 1e-66, 1e-65, 1e-64, 1e-63, 1e-62, 1e-61, 1e-60,
1e-59, 1e-58, 1e-57, 1e-56, 1e-55, 1e-54, 1e-53, 1e-52, 1e-51, 1e-50, 1e-49, 1e-48, 1e-47, 1e-46, 1e-45, 1e-44, 1e-43, 1e-42, 1e-41, 1e-40,
1e-39, 1e-38, 1e-37, 1e-36, 1e-35, 1e-34, 1e-33, 1e-32, 1e-31, 1e-30, 1e-29, 1e-28, 1e-27, 1e-26, 1e-25, 1e-24, 1e-23, 1e-22, 1e-21, 1e-20,
1e-19, 1e-18, 1e-17, 1e-16, 1e-15, 1e-14, 1e-13, 1e-12, 1e-11, 1e-10, 1e-9, 1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1e+0,
1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20, 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20,
1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40, 1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40,
1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60, 1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60,
...@@ -44,8 +29,8 @@ inline double Pow10(int n) { ...@@ -44,8 +29,8 @@ inline double Pow10(int n) {
1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300, 1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300,
1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308 1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308
}; };
RAPIDJSON_ASSERT(n <= 308); RAPIDJSON_ASSERT(n >= 0 && n <= 308);
return n < -308 ? 0.0 : e[n + 308]; return e[n];
} }
} // namespace internal } // namespace internal
......
...@@ -31,9 +31,6 @@ public: ...@@ -31,9 +31,6 @@ public:
PrettyWriter(OutputStream& os, Allocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : PrettyWriter(OutputStream& os, Allocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) :
Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {} Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {}
//! Overridden for fluent API, see \ref Writer::SetDoublePrecision()
PrettyWriter& SetDoublePrecision(int p) { Base::SetDoublePrecision(p); return *this; }
//! Set custom indentation. //! Set custom indentation.
/*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r'). /*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r').
\param indentCharCount Number of indent characters for each indentation level. \param indentCharCount Number of indent characters for each indentation level.
...@@ -119,15 +116,6 @@ public: ...@@ -119,15 +116,6 @@ public:
//! Simpler but slower overload. //! Simpler but slower overload.
bool String(const Ch* str) { return String(str, internal::StrLen(str)); } bool String(const Ch* str) { return String(str, internal::StrLen(str)); }
//! Overridden for fluent API, see \ref Writer::Double()
bool Double(double d, int precision) {
int oldPrecision = Base::GetDoublePrecision();
SetDoublePrecision(precision);
bool ret = Double(d);
SetDoublePrecision(oldPrecision);
return ret;
}
//@} //@}
protected: protected:
void PrettyPrefix(Type type) { void PrettyPrefix(Type type) {
......
...@@ -668,6 +668,18 @@ private: ...@@ -668,6 +668,18 @@ private:
} }
} }
inline double StrtodFastPath(double significand, int exp) {
// Fast path only works on limited range of values.
// But for simplicity and performance, currently only implement this.
// see http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/
if (exp < -308)
return 0.0;
else if (exp >= 0)
return significand * internal::Pow10(exp);
else
return significand / internal::Pow10(-exp);
}
template<unsigned parseFlags, typename InputStream, typename Handler> template<unsigned parseFlags, typename InputStream, typename Handler>
void ParseNumber(InputStream& is, Handler& handler) { void ParseNumber(InputStream& is, Handler& handler) {
internal::StreamLocalCopy<InputStream> copy(is); internal::StreamLocalCopy<InputStream> copy(is);
...@@ -813,11 +825,11 @@ private: ...@@ -813,11 +825,11 @@ private:
int expSum = exp + expFrac; int expSum = exp + expFrac;
if (expSum < -308) { if (expSum < -308) {
// Prevent expSum < -308, making Pow10(expSum) = 0 // Prevent expSum < -308, making Pow10(expSum) = 0
d *= internal::Pow10(exp); d = StrtodFastPath(d, exp);
d *= internal::Pow10(expFrac); d = StrtodFastPath(d, expFrac);
} }
else else
d *= internal::Pow10(expSum); d = StrtodFastPath(d, expSum);
cont = handler.Double(minus ? -d : d); cont = handler.Double(minus ? -d : d);
} }
......
...@@ -4,9 +4,9 @@ ...@@ -4,9 +4,9 @@
#include "rapidjson.h" #include "rapidjson.h"
#include "internal/stack.h" #include "internal/stack.h"
#include "internal/strfunc.h" #include "internal/strfunc.h"
#include "internal/dtoa.h"
#include "internal/itoa.h" #include "internal/itoa.h"
#include "stringbuffer.h" #include "stringbuffer.h"
#include <cstdio> // snprintf() or _sprintf_s()
#include <new> // placement new #include <new> // placement new
#ifdef _MSC_VER #ifdef _MSC_VER
...@@ -43,12 +43,10 @@ public: ...@@ -43,12 +43,10 @@ public:
\param levelDepth Initial capacity of stack. \param levelDepth Initial capacity of stack.
*/ */
Writer(OutputStream& os, Allocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) : Writer(OutputStream& os, Allocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) :
os_(&os), level_stack_(allocator, levelDepth * sizeof(Level)), os_(&os), level_stack_(allocator, levelDepth * sizeof(Level)), hasRoot_(false) {}
doublePrecision_(kDefaultDoublePrecision), hasRoot_(false) {}
Writer(Allocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) : Writer(Allocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) :
os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), hasRoot_(false) {}
doublePrecision_(kDefaultDoublePrecision), hasRoot_(false) {}
//! Reset the writer with a new stream. //! Reset the writer with a new stream.
/*! /*!
...@@ -70,7 +68,6 @@ public: ...@@ -70,7 +68,6 @@ public:
*/ */
void Reset(OutputStream& os) { void Reset(OutputStream& os) {
os_ = &os; os_ = &os;
doublePrecision_ = kDefaultDoublePrecision;
hasRoot_ = false; hasRoot_ = false;
level_stack_.Clear(); level_stack_.Clear();
} }
...@@ -83,21 +80,6 @@ public: ...@@ -83,21 +80,6 @@ public:
return hasRoot_ && level_stack_.Empty(); return hasRoot_ && level_stack_.Empty();
} }
//! Set the number of significant digits for \c double values
/*! When writing a \c double value to the \c OutputStream, the number
of significant digits is limited to 6 by default.
\param p maximum number of significant digits (default: 6)
\return The Writer itself for fluent API.
*/
Writer& SetDoublePrecision(int p = kDefaultDoublePrecision) {
if (p < 0) p = kDefaultDoublePrecision; // negative precision is ignored
doublePrecision_ = p;
return *this;
}
//! \see SetDoublePrecision()
int GetDoublePrecision() const { return doublePrecision_; }
/*!@name Implementation of Handler /*!@name Implementation of Handler
\see Handler \see Handler
*/ */
...@@ -112,12 +94,6 @@ public: ...@@ -112,12 +94,6 @@ public:
//! Writes the given \c double value to the stream //! Writes the given \c double value to the stream
/*! /*!
The number of significant digits (the precision) to be written
can be set by \ref SetDoublePrecision() for the Writer:
\code
Writer<...> writer(...);
writer.SetDoublePrecision(12).Double(M_PI);
\endcode
\param d The value to be written. \param d The value to be written.
\return Whether it is succeed. \return Whether it is succeed.
*/ */
...@@ -167,23 +143,6 @@ public: ...@@ -167,23 +143,6 @@ public:
/*! @name Convenience extensions */ /*! @name Convenience extensions */
//@{ //@{
//! Writes the given \c double value to the stream (explicit precision)
/*!
The currently set double precision is ignored in favor of the explicitly
given precision for this value.
\see Double(), SetDoublePrecision(), GetDoublePrecision()
\param d The value to be written
\param precision The number of significant digits for this value
\return Whether it is succeeded.
*/
bool Double(double d, int precision) {
int oldPrecision = GetDoublePrecision();
SetDoublePrecision(precision);
bool ret = Double(d);
SetDoublePrecision(oldPrecision);
return ret;
}
//! Simpler but slower overload. //! Simpler but slower overload.
bool String(const Ch* str) { return String(str, internal::StrLen(str)); } bool String(const Ch* str) { return String(str, internal::StrLen(str)); }
...@@ -239,25 +198,17 @@ protected: ...@@ -239,25 +198,17 @@ protected:
bool WriteUint64(uint64_t u64) { bool WriteUint64(uint64_t u64) {
char buffer[20]; char buffer[20];
const char* end = internal::u64toa(u64, buffer); char* end = internal::u64toa(u64, buffer);
for (const char* p = buffer; p != end; ++p) for (char* p = buffer; p != end; ++p)
os_->Put(*p); os_->Put(*p);
return true; return true;
} }
#ifdef _MSC_VER
#define RAPIDJSON_SNPRINTF sprintf_s
#else
#define RAPIDJSON_SNPRINTF snprintf
#endif
//! \todo Optimization with custom double-to-string converter.
bool WriteDouble(double d) { bool WriteDouble(double d) {
char buffer[100]; char buffer[25];
int ret = RAPIDJSON_SNPRINTF(buffer, sizeof(buffer), "%.*g", doublePrecision_, d); char* end = internal::dtoa(d, buffer);
RAPIDJSON_ASSERT(ret >= 1); for (char* p = buffer; p != end; ++p)
for (int i = 0; i < ret; i++) os_->Put(*p);
os_->Put(buffer[i]);
return true; return true;
} }
#undef RAPIDJSON_SNPRINTF #undef RAPIDJSON_SNPRINTF
...@@ -358,11 +309,8 @@ protected: ...@@ -358,11 +309,8 @@ protected:
OutputStream* os_; OutputStream* os_;
internal::Stack<Allocator> level_stack_; internal::Stack<Allocator> level_stack_;
int doublePrecision_;
bool hasRoot_; bool hasRoot_;
static const int kDefaultDoublePrecision = 6;
private: private:
// Prohibit copy constructor & assignment operator. // Prohibit copy constructor & assignment operator.
Writer(const Writer&); Writer(const Writer&);
...@@ -403,6 +351,14 @@ inline bool Writer<StringBuffer>::WriteUint64(uint64_t u) { ...@@ -403,6 +351,14 @@ inline bool Writer<StringBuffer>::WriteUint64(uint64_t u) {
return true; return true;
} }
template<>
inline bool Writer<StringBuffer>::WriteDouble(double d) {
char *buffer = os_->Push(25);
char* end = internal::dtoa(d, buffer);
os_->Pop(25 - (end - buffer));
return true;
}
} // namespace rapidjson } // namespace rapidjson
#ifdef _MSC_VER #ifdef _MSC_VER
......
...@@ -60,60 +60,9 @@ TEST(Writer, String) { ...@@ -60,60 +60,9 @@ TEST(Writer, String) {
TEST_ROUNDTRIP("[\"\\\"\\\\/\\b\\f\\n\\r\\t\"]"); TEST_ROUNDTRIP("[\"\\\"\\\\/\\b\\f\\n\\r\\t\"]");
} }
TEST(Writer,DoublePrecision) { TEST(Writer, Double) {
const char json[] = "[1.2345,1.2345678,0.123456789012,1234567.8]"; TEST_ROUNDTRIP("[1.2345,1.2345678,0.123456789012,1234567.8]");
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
const int kDefaultDoublePrecision = 6;
// handling the double precision
EXPECT_EQ(writer.GetDoublePrecision(), kDefaultDoublePrecision);
writer.SetDoublePrecision(17);
EXPECT_EQ(writer.GetDoublePrecision(), 17);
writer.SetDoublePrecision(-1); // negative equivalent to reset
EXPECT_EQ(writer.GetDoublePrecision(), kDefaultDoublePrecision);
writer.SetDoublePrecision(1);
writer.SetDoublePrecision(); // reset again
EXPECT_EQ(writer.GetDoublePrecision(), kDefaultDoublePrecision);
{ // write with explicitly increased precision
StringStream s(json);
Reader reader;
reader.Parse<0>(s, writer.SetDoublePrecision(12));
EXPECT_EQ(writer.GetDoublePrecision(), 12);
EXPECT_STREQ(json, buffer.GetString());
}
{ // explicit individual double precisions
buffer.Clear();
writer.Reset(buffer);
writer.SetDoublePrecision(2);
writer.StartArray();
writer.Double(1.2345, 5);
writer.Double(1.2345678, 9);
writer.Double(0.123456789012, 12);
writer.Double(1234567.8, 8);
writer.EndArray();
EXPECT_EQ(writer.GetDoublePrecision(), 2);
EXPECT_STREQ(json, buffer.GetString());
}
{ // write with default precision (output with precision loss)
Document d;
d.Parse<0>(json);
buffer.Clear();
writer.Reset(buffer);
d.Accept(writer.SetDoublePrecision());
// parsed again to avoid platform-dependent floating point outputs
// (e.g. width of exponents)
d.Parse<0>(buffer.GetString());
EXPECT_EQ(writer.GetDoublePrecision(), kDefaultDoublePrecision);
EXPECT_DOUBLE_EQ(d[0u].GetDouble(), 1.2345);
EXPECT_DOUBLE_EQ(d[1u].GetDouble(), 1.23457);
EXPECT_DOUBLE_EQ(d[2u].GetDouble(), 0.123457);
EXPECT_DOUBLE_EQ(d[3u].GetDouble(), 1234570);
}
} }
TEST(Writer, Transcode) { TEST(Writer, Transcode) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment