Commit 40852f4d authored by Milo Yip's avatar Milo Yip

Fixes StrtodDiyFp bugs

parent b4e2d58c
......@@ -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);
......
......@@ -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:
......
......@@ -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++;
......
......@@ -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
{
......
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