Fixes StrtodDiyFp bugs
This commit is contained in:
parent
b4e2d58c74
commit
40852f4d6d
@ -179,7 +179,7 @@ struct DiyFp {
|
|||||||
static const int kDpExponentBias = 0x3FF + kDpSignificandSize;
|
static const int kDpExponentBias = 0x3FF + kDpSignificandSize;
|
||||||
static const int kDpMaxExponent = 0x7FF - kDpExponentBias;
|
static const int kDpMaxExponent = 0x7FF - kDpExponentBias;
|
||||||
static const int kDpMinExponent = -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 kDpExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000);
|
||||||
static const uint64_t kDpSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF);
|
static const uint64_t kDpSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF);
|
||||||
static const uint64_t kDpHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000);
|
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; }
|
uint64_t ToBias() const { return (u & kSignMask) ? ~u + 1 : u | kSignMask; }
|
||||||
|
|
||||||
static unsigned EffectiveSignificandSize(int order) {
|
static unsigned EffectiveSignificandSize(int order) {
|
||||||
if (order >= kDenormalExponent + kSignificandSize)
|
if (order >= -1021)
|
||||||
return kSignificandSize;
|
return 53;
|
||||||
else if (order <= kDenormalExponent)
|
else if (order <= -1074)
|
||||||
return 0;
|
return 0;
|
||||||
else
|
else
|
||||||
return order - kDenormalExponent;
|
return order + 1074;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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) {
|
inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosition, int exp, double* result) {
|
||||||
uint64_t significand = 0;
|
uint64_t significand = 0;
|
||||||
size_t i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999
|
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');
|
significand = significand * 10 + (decimals[i] - '0');
|
||||||
|
}
|
||||||
|
|
||||||
if (i < length && decimals[i] >= '5') // Rounding
|
if (i < length && decimals[i] >= '5') // Rounding
|
||||||
significand++;
|
significand++;
|
||||||
|
|
||||||
DiyFp v(significand, 0);
|
|
||||||
size_t remaining = length - i;
|
size_t remaining = length - i;
|
||||||
const int dExp = (int)decimalPosition - (int)i + exp + (int)remaining;
|
|
||||||
|
|
||||||
const unsigned kUlpShift = 3;
|
const unsigned kUlpShift = 3;
|
||||||
const unsigned kUlp = 1 << kUlpShift;
|
const unsigned kUlp = 1 << kUlpShift;
|
||||||
int error = (remaining == 0) ? 0 : kUlp / 2;
|
int error = (remaining == 0) ? 0 : kUlp / 2;
|
||||||
|
|
||||||
|
DiyFp v(significand, 0);
|
||||||
v = v.Normalize();
|
v = v.Normalize();
|
||||||
error <<= - v.e;
|
error <<= -v.e;
|
||||||
|
|
||||||
|
const int dExp = (int)decimalPosition - (int)i + exp;
|
||||||
|
|
||||||
int actualExp;
|
int actualExp;
|
||||||
double temp1 = v.ToDouble();
|
DiyFp cachedPower = GetCachedPower10(dExp, &actualExp);
|
||||||
v = v * GetCachedPower10(dExp, &actualExp);
|
|
||||||
double temp2 = v.ToDouble();
|
|
||||||
if (actualExp != dExp) {
|
if (actualExp != dExp) {
|
||||||
static const DiyFp kPow10[] = {
|
static const DiyFp kPow10[] = {
|
||||||
DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 00000000), -60), // 10^1
|
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(0xf4240000, 00000000), -44), // 10^6
|
||||||
DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 00000000), -40) // 10^7
|
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);
|
error += kUlp + (error == 0 ? 0 : 1);
|
||||||
|
|
||||||
@ -197,9 +203,9 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit
|
|||||||
precisionSize -= scaleExp;
|
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 precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp;
|
||||||
const uint64_t halfWay = (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)
|
if (precisionBits >= halfWay + error)
|
||||||
rounded.f++;
|
rounded.f++;
|
||||||
|
|
||||||
|
@ -195,7 +195,6 @@ static void TestParseDouble() {
|
|||||||
EXPECT_DOUBLE_EQ(x, h.actual_); \
|
EXPECT_DOUBLE_EQ(x, h.actual_); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
TEST_DOUBLE(fullPrecision, "0.0", 0.0);
|
TEST_DOUBLE(fullPrecision, "0.0", 0.0);
|
||||||
TEST_DOUBLE(fullPrecision, "1.0", 1.0);
|
TEST_DOUBLE(fullPrecision, "1.0", 1.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, "2.22507e-308", 2.22507e-308);
|
||||||
TEST_DOUBLE(fullPrecision, "-1.79769e+308", -1.79769e+308);
|
TEST_DOUBLE(fullPrecision, "-1.79769e+308", -1.79769e+308);
|
||||||
TEST_DOUBLE(fullPrecision, "-2.22507e-308", -2.22507e-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, "4.9406564584124654e-324", 4.9406564584124654e-324); // minimum denormal
|
||||||
TEST_DOUBLE(fullPrecision, "2.2250738585072009e-308", 2.2250738585072009e-308); // Max subnormal double
|
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
|
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);
|
TEST_DOUBLE(fullPrecision, n1e308, 1E308);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
#if 1
|
||||||
static const unsigned count = 10000000;
|
static const unsigned count = 10000000;
|
||||||
// Random test for double
|
// Random test for double
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user