From 40852f4d6d3547637074c5937259c96e41edb714 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Fri, 14 Nov 2014 18:23:51 +0800 Subject: [PATCH] Fixes StrtodDiyFp bugs --- include/rapidjson/internal/diyfp.h | 2 +- include/rapidjson/internal/ieee754.h | 8 ++++---- include/rapidjson/internal/strtod.h | 28 +++++++++++++++++----------- test/unittest/readertest.cpp | 4 +--- 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/include/rapidjson/internal/diyfp.h b/include/rapidjson/internal/diyfp.h index fb662db..9097583 100644 --- a/include/rapidjson/internal/diyfp.h +++ b/include/rapidjson/internal/diyfp.h @@ -179,7 +179,7 @@ struct DiyFp { static const int kDpExponentBias = 0x3FF + kDpSignificandSize; static const int kDpMaxExponent = 0x7FF - kDpExponentBias; static const int kDpMinExponent = -kDpExponentBias; - static const int kDpDenormalExponent = -kDpExponentBias - 1; + static const int kDpDenormalExponent = -kDpExponentBias + 1; static const uint64_t kDpExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); static const uint64_t kDpSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); static const uint64_t kDpHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); diff --git a/include/rapidjson/internal/ieee754.h b/include/rapidjson/internal/ieee754.h index 5e5d207..41aea12 100644 --- a/include/rapidjson/internal/ieee754.h +++ b/include/rapidjson/internal/ieee754.h @@ -61,12 +61,12 @@ public: uint64_t ToBias() const { return (u & kSignMask) ? ~u + 1 : u | kSignMask; } static unsigned EffectiveSignificandSize(int order) { - if (order >= kDenormalExponent + kSignificandSize) - return kSignificandSize; - else if (order <= kDenormalExponent) + if (order >= -1021) + return 53; + else if (order <= -1074) return 0; else - return order - kDenormalExponent; + return order + 1074; } private: diff --git a/include/rapidjson/internal/strtod.h b/include/rapidjson/internal/strtod.h index 9b4f2c0..e8b1e7a 100644 --- a/include/rapidjson/internal/strtod.h +++ b/include/rapidjson/internal/strtod.h @@ -146,27 +146,29 @@ inline bool StrtodFast(double d, int p, double* result) { inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosition, int exp, double* result) { uint64_t significand = 0; size_t i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999 - for (; i < length && (significand < RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || decimals[i] <= '4'); i++) + for (; i < length; i++) { + if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || + significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5') + break; significand = significand * 10 + (decimals[i] - '0'); + } if (i < length && decimals[i] >= '5') // Rounding significand++; - DiyFp v(significand, 0); size_t remaining = length - i; - const int dExp = (int)decimalPosition - (int)i + exp + (int)remaining; - const unsigned kUlpShift = 3; const unsigned kUlp = 1 << kUlpShift; int error = (remaining == 0) ? 0 : kUlp / 2; + DiyFp v(significand, 0); v = v.Normalize(); - error <<= - v.e; + error <<= -v.e; + + const int dExp = (int)decimalPosition - (int)i + exp; int actualExp; - double temp1 = v.ToDouble(); - v = v * GetCachedPower10(dExp, &actualExp); - double temp2 = v.ToDouble(); + DiyFp cachedPower = GetCachedPower10(dExp, &actualExp); if (actualExp != dExp) { static const DiyFp kPow10[] = { DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 00000000), -60), // 10^1 @@ -177,9 +179,13 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 00000000), -44), // 10^6 DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 00000000), -40) // 10^7 }; - v = v * kPow10[dExp - actualExp - 1]; + int adjustment = dExp - actualExp - 1; + v = v * kPow10[adjustment]; + if (length + adjustment > 19) // has more digits than decimal digits in 64-bit + error += kUlp / 2; } - double temp3 = v.ToDouble(); + + v = v * cachedPower; error += kUlp + (error == 0 ? 0 : 1); @@ -197,9 +203,9 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit precisionSize -= scaleExp; } + DiyFp rounded(v.f >> precisionSize, v.e + precisionSize); const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp; const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp; - DiyFp rounded(v.f >> precisionSize, v.e + precisionSize); if (precisionBits >= halfWay + error) rounded.f++; diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index ab40937..edd47f3 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -195,7 +195,6 @@ static void TestParseDouble() { EXPECT_DOUBLE_EQ(x, h.actual_); \ } -#if 0 TEST_DOUBLE(fullPrecision, "0.0", 0.0); TEST_DOUBLE(fullPrecision, "1.0", 1.0); TEST_DOUBLE(fullPrecision, "-1.0", -1.0); @@ -216,7 +215,6 @@ static void TestParseDouble() { TEST_DOUBLE(fullPrecision, "2.22507e-308", 2.22507e-308); TEST_DOUBLE(fullPrecision, "-1.79769e+308", -1.79769e+308); TEST_DOUBLE(fullPrecision, "-2.22507e-308", -2.22507e-308); -#endif TEST_DOUBLE(fullPrecision, "4.9406564584124654e-324", 4.9406564584124654e-324); // minimum denormal TEST_DOUBLE(fullPrecision, "2.2250738585072009e-308", 2.2250738585072009e-308); // Max subnormal double TEST_DOUBLE(fullPrecision, "2.2250738585072014e-308", 2.2250738585072014e-308); // Min normal positive double @@ -259,7 +257,7 @@ static void TestParseDouble() { TEST_DOUBLE(fullPrecision, n1e308, 1E308); } -#if 0 +#if 1 static const unsigned count = 10000000; // Random test for double {