Skip to content

Commit 6cc3910

Browse files
authored
Merge pull request Tencent#1290 from abolz/fix-strtod
Fix strtod
2 parents 01c7174 + 7101911 commit 6cc3910

File tree

6 files changed

+395
-60
lines changed

6 files changed

+395
-60
lines changed

include/rapidjson/internal/biginteger.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ class BigInteger {
133133
RAPIDJSON_ASSERT(count_ + offset <= kCapacity);
134134

135135
if (interShift == 0) {
136-
std::memmove(&digits_[count_ - 1 + offset], &digits_[count_ - 1], count_ * sizeof(Type));
136+
std::memmove(digits_ + offset, digits_, count_ * sizeof(Type));
137137
count_ += offset;
138138
}
139139
else {

include/rapidjson/internal/diyfp.h

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
// Tencent is pleased to support the open source community by making RapidJSON available.
2-
//
2+
//
33
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
44
//
55
// Licensed under the MIT License (the "License"); you may not use this file except
66
// in compliance with the License. You may obtain a copy of the License at
77
//
88
// http://opensource.org/licenses/MIT
99
//
10-
// Unless required by applicable law or agreed to in writing, software distributed
11-
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
12-
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
10+
// Unless required by applicable law or agreed to in writing, software distributed
11+
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
12+
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
1313
// specific language governing permissions and limitations under the License.
1414

1515
// This is a C++ header-only implementation of Grisu2 algorithm from the publication:
@@ -20,6 +20,7 @@
2020
#define RAPIDJSON_DIYFP_H_
2121

2222
#include "../rapidjson.h"
23+
#include <limits>
2324

2425
#if defined(_MSC_VER) && defined(_M_AMD64) && !defined(__INTEL_COMPILER)
2526
#include <intrin.h>
@@ -56,7 +57,7 @@ struct DiyFp {
5657
if (biased_e != 0) {
5758
f = significand + kDpHiddenBit;
5859
e = biased_e - kDpExponentBias;
59-
}
60+
}
6061
else {
6162
f = significand;
6263
e = kDpMinExponent + 1;
@@ -141,7 +142,16 @@ struct DiyFp {
141142
double d;
142143
uint64_t u64;
143144
}u;
144-
const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 :
145+
RAPIDJSON_ASSERT(f <= kDpHiddenBit + kDpSignificandMask);
146+
if (e < kDpDenormalExponent) {
147+
// Underflow.
148+
return 0.0;
149+
}
150+
if (e >= kDpMaxExponent) {
151+
// Overflow.
152+
return std::numeric_limits<double>::infinity();
153+
}
154+
const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 :
145155
static_cast<uint64_t>(e + kDpExponentBias);
146156
u.u64 = (f & kDpSignificandMask) | (be << kDpSignificandSize);
147157
return u.d;
@@ -220,9 +230,10 @@ inline DiyFp GetCachedPowerByIndex(size_t index) {
220230
641, 667, 694, 720, 747, 774, 800, 827, 853, 880,
221231
907, 933, 960, 986, 1013, 1039, 1066
222232
};
233+
RAPIDJSON_ASSERT(index < 87);
223234
return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]);
224235
}
225-
236+
226237
inline DiyFp GetCachedPower(int e, int* K) {
227238

228239
//int k = static_cast<int>(ceil((-61 - e) * 0.30102999566398114)) + 374;
@@ -238,10 +249,11 @@ inline DiyFp GetCachedPower(int e, int* K) {
238249
}
239250

240251
inline DiyFp GetCachedPower10(int exp, int *outExp) {
241-
unsigned index = (static_cast<unsigned>(exp) + 348u) / 8u;
242-
*outExp = -348 + static_cast<int>(index) * 8;
243-
return GetCachedPowerByIndex(index);
244-
}
252+
RAPIDJSON_ASSERT(exp >= -348);
253+
unsigned index = static_cast<unsigned>(exp + 348) / 8u;
254+
*outExp = -348 + static_cast<int>(index) * 8;
255+
return GetCachedPowerByIndex(index);
256+
}
245257

246258
#ifdef __GNUC__
247259
RAPIDJSON_DIAG_POP

include/rapidjson/internal/strtod.h

Lines changed: 59 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
#include "biginteger.h"
2020
#include "diyfp.h"
2121
#include "pow10.h"
22+
#include <climits>
23+
#include <limits>
2224

2325
RAPIDJSON_NAMESPACE_BEGIN
2426
namespace internal {
@@ -126,20 +128,20 @@ inline bool StrtodFast(double d, int p, double* result) {
126128
}
127129

128130
// Compute an approximation and see if it is within 1/2 ULP
129-
inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosition, int exp, double* result) {
131+
inline bool StrtodDiyFp(const char* decimals, int dLen, int dExp, double* result) {
130132
uint64_t significand = 0;
131-
size_t i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999
132-
for (; i < length; i++) {
133+
int i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999
134+
for (; i < dLen; i++) {
133135
if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) ||
134136
(significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5'))
135137
break;
136138
significand = significand * 10u + static_cast<unsigned>(decimals[i] - '0');
137139
}
138140

139-
if (i < length && decimals[i] >= '5') // Rounding
141+
if (i < dLen && decimals[i] >= '5') // Rounding
140142
significand++;
141143

142-
size_t remaining = length - i;
144+
int remaining = dLen - i;
143145
const int kUlpShift = 3;
144146
const int kUlp = 1 << kUlpShift;
145147
int64_t error = (remaining == 0) ? 0 : kUlp / 2;
@@ -148,24 +150,24 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit
148150
v = v.Normalize();
149151
error <<= -v.e;
150152

151-
const int dExp = static_cast<int>(decimalPosition) - static_cast<int>(i) + exp;
153+
dExp += remaining;
152154

153155
int actualExp;
154156
DiyFp cachedPower = GetCachedPower10(dExp, &actualExp);
155157
if (actualExp != dExp) {
156158
static const DiyFp kPow10[] = {
157-
DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 00000000), -60), // 10^1
158-
DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 00000000), -57), // 10^2
159-
DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 00000000), -54), // 10^3
160-
DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 00000000), -50), // 10^4
161-
DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 00000000), -47), // 10^5
162-
DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 00000000), -44), // 10^6
163-
DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 00000000), -40) // 10^7
159+
DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 0x00000000), -60), // 10^1
160+
DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 0x00000000), -57), // 10^2
161+
DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 0x00000000), -54), // 10^3
162+
DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), -50), // 10^4
163+
DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 0x00000000), -47), // 10^5
164+
DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 0x00000000), -44), // 10^6
165+
DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 0x00000000), -40) // 10^7
164166
};
165-
int adjustment = dExp - actualExp - 1;
166-
RAPIDJSON_ASSERT(adjustment >= 0 && adjustment < 7);
167-
v = v * kPow10[adjustment];
168-
if (length + static_cast<unsigned>(adjustment)> 19u) // has more digits than decimal digits in 64-bit
167+
int adjustment = dExp - actualExp;
168+
RAPIDJSON_ASSERT(adjustment >= 1 && adjustment < 8);
169+
v = v * kPow10[adjustment - 1];
170+
if (dLen + adjustment > 19) // has more digits than decimal digits in 64-bit
169171
error += kUlp / 2;
170172
}
171173

@@ -203,9 +205,9 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit
203205
return halfWay - static_cast<unsigned>(error) >= precisionBits || precisionBits >= halfWay + static_cast<unsigned>(error);
204206
}
205207

206-
inline double StrtodBigInteger(double approx, const char* decimals, size_t length, size_t decimalPosition, int exp) {
207-
const BigInteger dInt(decimals, length);
208-
const int dExp = static_cast<int>(decimalPosition) - static_cast<int>(length) + exp;
208+
inline double StrtodBigInteger(double approx, const char* decimals, int dLen, int dExp) {
209+
RAPIDJSON_ASSERT(dLen >= 0);
210+
const BigInteger dInt(decimals, static_cast<unsigned>(dLen));
209211
Double a(approx);
210212
int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp);
211213
if (cmp < 0)
@@ -225,42 +227,61 @@ inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t
225227
RAPIDJSON_ASSERT(d >= 0.0);
226228
RAPIDJSON_ASSERT(length >= 1);
227229

228-
double result;
230+
double result = 0.0;
229231
if (StrtodFast(d, p, &result))
230232
return result;
231233

234+
RAPIDJSON_ASSERT(length <= INT_MAX);
235+
int dLen = static_cast<int>(length);
236+
237+
RAPIDJSON_ASSERT(length >= decimalPosition);
238+
RAPIDJSON_ASSERT(length - decimalPosition <= INT_MAX);
239+
int dExpAdjust = static_cast<int>(length - decimalPosition);
240+
241+
RAPIDJSON_ASSERT(exp >= INT_MIN + dExpAdjust);
242+
int dExp = exp - dExpAdjust;
243+
244+
// Make sure length+dExp does not overflow
245+
RAPIDJSON_ASSERT(dExp <= INT_MAX - dLen);
246+
232247
// Trim leading zeros
233-
while (*decimals == '0' && length > 1) {
234-
length--;
248+
while (dLen > 0 && *decimals == '0') {
249+
dLen--;
235250
decimals++;
236-
decimalPosition--;
237251
}
238252

239253
// Trim trailing zeros
240-
while (decimals[length - 1] == '0' && length > 1) {
241-
length--;
242-
decimalPosition--;
243-
exp++;
254+
while (dLen > 0 && decimals[dLen - 1] == '0') {
255+
dLen--;
256+
dExp++;
257+
}
258+
259+
if (dLen == 0) { // Buffer only contains zeros.
260+
return 0.0;
244261
}
245262

246263
// Trim right-most digits
247-
const int kMaxDecimalDigit = 780;
248-
if (static_cast<int>(length) > kMaxDecimalDigit) {
249-
int delta = (static_cast<int>(length) - kMaxDecimalDigit);
250-
exp += delta;
251-
decimalPosition -= static_cast<unsigned>(delta);
252-
length = kMaxDecimalDigit;
264+
const int kMaxDecimalDigit = 767 + 1;
265+
if (dLen > kMaxDecimalDigit) {
266+
dExp += dLen - kMaxDecimalDigit;
267+
dLen = kMaxDecimalDigit;
253268
}
254269

255-
// If too small, underflow to zero
256-
if (int(length) + exp < -324)
270+
// If too small, underflow to zero.
271+
// Any x <= 10^-324 is interpreted as zero.
272+
if (dLen + dExp <= -324)
257273
return 0.0;
258274

259-
if (StrtodDiyFp(decimals, length, decimalPosition, exp, &result))
275+
// If too large, overflow to infinity.
276+
// Any x >= 10^309 is interpreted as +infinity.
277+
if (dLen + dExp > 309)
278+
return std::numeric_limits<double>::infinity();
279+
280+
if (StrtodDiyFp(decimals, dLen, dExp, &result))
260281
return result;
261282

262283
// Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison
263-
return StrtodBigInteger(result, decimals, length, decimalPosition, exp);
284+
return StrtodBigInteger(result, decimals, dLen, dExp);
264285
}
265286

266287
} // namespace internal

include/rapidjson/reader.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1561,8 +1561,6 @@ class GenericReader {
15611561
// Force double for big integer
15621562
if (useDouble) {
15631563
while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
1564-
if (RAPIDJSON_UNLIKELY(d >= 1.7976931348623157e307)) // DBL_MAX / 10.0
1565-
RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset);
15661564
d = d * 10 + (s.TakePush() - '0');
15671565
}
15681566
}
@@ -1702,6 +1700,13 @@ class GenericReader {
17021700
else
17031701
d = internal::StrtodNormalPrecision(d, p);
17041702

1703+
// Use > max, instead of == inf, to fix bogus warning -Wfloat-equal
1704+
if (d > std::numeric_limits<double>::max()) {
1705+
// Overflow
1706+
// TODO: internal::StrtodX should report overflow (or underflow)
1707+
RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset);
1708+
}
1709+
17051710
cont = handler.Double(minus ? -d : d);
17061711
}
17071712
else if (useNanOrInf) {

test/unittest/bigintegertest.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,11 @@ TEST(BigInteger, LeftShift) {
120120
EXPECT_TRUE(BIGINTEGER_LITERAL("4537899042132549697536") == a);
121121
a <<= 99;
122122
EXPECT_TRUE(BIGINTEGER_LITERAL("2876235222267216943024851750785644982682875244576768") == a);
123+
124+
a = 1;
125+
a <<= 64; // a.count_ != 1
126+
a <<= 256; // interShift == 0
127+
EXPECT_TRUE(BIGINTEGER_LITERAL("2135987035920910082395021706169552114602704522356652769947041607822219725780640550022962086936576") == a);
123128
}
124129

125130
TEST(BigInteger, Compare) {

0 commit comments

Comments
 (0)