19
19
#include " biginteger.h"
20
20
#include " diyfp.h"
21
21
#include " pow10.h"
22
+ #include < climits>
23
+ #include < limits>
22
24
23
25
RAPIDJSON_NAMESPACE_BEGIN
24
26
namespace internal {
@@ -126,20 +128,20 @@ inline bool StrtodFast(double d, int p, double* result) {
126
128
}
127
129
128
130
// 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) {
130
132
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++) {
133
135
if (significand > RAPIDJSON_UINT64_C2 (0x19999999 , 0x99999999 ) ||
134
136
(significand == RAPIDJSON_UINT64_C2 (0x19999999 , 0x99999999 ) && decimals[i] > ' 5' ))
135
137
break ;
136
138
significand = significand * 10u + static_cast <unsigned >(decimals[i] - ' 0' );
137
139
}
138
140
139
- if (i < length && decimals[i] >= ' 5' ) // Rounding
141
+ if (i < dLen && decimals[i] >= ' 5' ) // Rounding
140
142
significand++;
141
143
142
- size_t remaining = length - i;
144
+ int remaining = dLen - i;
143
145
const int kUlpShift = 3 ;
144
146
const int kUlp = 1 << kUlpShift ;
145
147
int64_t error = (remaining == 0 ) ? 0 : kUlp / 2 ;
@@ -148,24 +150,24 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit
148
150
v = v.Normalize ();
149
151
error <<= -v.e ;
150
152
151
- const int dExp = static_cast < int >(decimalPosition) - static_cast < int >(i) + exp ;
153
+ dExp += remaining ;
152
154
153
155
int actualExp;
154
156
DiyFp cachedPower = GetCachedPower10 (dExp, &actualExp);
155
157
if (actualExp != dExp) {
156
158
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
164
166
};
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
169
171
error += kUlp / 2 ;
170
172
}
171
173
@@ -203,9 +205,9 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit
203
205
return halfWay - static_cast <unsigned >(error) >= precisionBits || precisionBits >= halfWay + static_cast <unsigned >(error);
204
206
}
205
207
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)) ;
209
211
Double a (approx);
210
212
int cmp = CheckWithinHalfULP (a.Value (), dInt, dExp);
211
213
if (cmp < 0 )
@@ -225,42 +227,61 @@ inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t
225
227
RAPIDJSON_ASSERT (d >= 0.0 );
226
228
RAPIDJSON_ASSERT (length >= 1 );
227
229
228
- double result;
230
+ double result = 0.0 ;
229
231
if (StrtodFast (d, p, &result))
230
232
return result;
231
233
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
+
232
247
// Trim leading zeros
233
- while (*decimals == ' 0 ' && length > 1 ) {
234
- length --;
248
+ while (dLen > 0 && *decimals == ' 0 ' ) {
249
+ dLen --;
235
250
decimals++;
236
- decimalPosition--;
237
251
}
238
252
239
253
// 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 ;
244
261
}
245
262
246
263
// 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 ;
253
268
}
254
269
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 )
257
273
return 0.0 ;
258
274
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))
260
281
return result;
261
282
262
283
// 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 );
264
285
}
265
286
266
287
} // namespace internal
0 commit comments