/src/assimp/contrib/rapidjson/include/rapidjson/internal/strtod.h
Line | Count | Source |
1 | | // Tencent is pleased to support the open source community by making RapidJSON available. |
2 | | // |
3 | | // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. |
4 | | // |
5 | | // Licensed under the MIT License (the "License"); you may not use this file except |
6 | | // in compliance with the License. You may obtain a copy of the License at |
7 | | // |
8 | | // http://opensource.org/licenses/MIT |
9 | | // |
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 |
13 | | // specific language governing permissions and limitations under the License. |
14 | | |
15 | | #ifndef RAPIDJSON_STRTOD_ |
16 | | #define RAPIDJSON_STRTOD_ |
17 | | |
18 | | #include "ieee754.h" |
19 | | #include "biginteger.h" |
20 | | #include "diyfp.h" |
21 | | #include "pow10.h" |
22 | | #include <climits> |
23 | | #include <limits> |
24 | | |
25 | | RAPIDJSON_NAMESPACE_BEGIN |
26 | | namespace internal { |
27 | | |
28 | 1.72M | inline double FastPath(double significand, int exp) { |
29 | 1.72M | if (exp < -308) |
30 | 5.41k | return 0.0; |
31 | 1.71M | else if (exp >= 0) |
32 | 163k | return significand * internal::Pow10(exp); |
33 | 1.55M | else |
34 | 1.55M | return significand / internal::Pow10(-exp); |
35 | 1.72M | } |
36 | | |
37 | 1.71M | inline double StrtodNormalPrecision(double d, int p) { |
38 | 1.71M | if (p < -308) { |
39 | | // Prevent expSum < -308, making Pow10(p) = 0 |
40 | 12.0k | d = FastPath(d, -308); |
41 | 12.0k | d = FastPath(d, p + 308); |
42 | 12.0k | } |
43 | 1.69M | else |
44 | 1.69M | d = FastPath(d, p); |
45 | 1.71M | return d; |
46 | 1.71M | } |
47 | | |
48 | | template <typename T> |
49 | 0 | inline T Min3(T a, T b, T c) { |
50 | 0 | T m = a; |
51 | 0 | if (m > b) m = b; |
52 | 0 | if (m > c) m = c; |
53 | 0 | return m; |
54 | 0 | } |
55 | | |
56 | 0 | inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp) { |
57 | 0 | const Double db(b); |
58 | 0 | const uint64_t bInt = db.IntegerSignificand(); |
59 | 0 | const int bExp = db.IntegerExponent(); |
60 | 0 | const int hExp = bExp - 1; |
61 | 0 |
|
62 | 0 | int dS_Exp2 = 0, dS_Exp5 = 0, bS_Exp2 = 0, bS_Exp5 = 0, hS_Exp2 = 0, hS_Exp5 = 0; |
63 | 0 |
|
64 | 0 | // Adjust for decimal exponent |
65 | 0 | if (dExp >= 0) { |
66 | 0 | dS_Exp2 += dExp; |
67 | 0 | dS_Exp5 += dExp; |
68 | 0 | } |
69 | 0 | else { |
70 | 0 | bS_Exp2 -= dExp; |
71 | 0 | bS_Exp5 -= dExp; |
72 | 0 | hS_Exp2 -= dExp; |
73 | 0 | hS_Exp5 -= dExp; |
74 | 0 | } |
75 | 0 |
|
76 | 0 | // Adjust for binary exponent |
77 | 0 | if (bExp >= 0) |
78 | 0 | bS_Exp2 += bExp; |
79 | 0 | else { |
80 | 0 | dS_Exp2 -= bExp; |
81 | 0 | hS_Exp2 -= bExp; |
82 | 0 | } |
83 | 0 |
|
84 | 0 | // Adjust for half ulp exponent |
85 | 0 | if (hExp >= 0) |
86 | 0 | hS_Exp2 += hExp; |
87 | 0 | else { |
88 | 0 | dS_Exp2 -= hExp; |
89 | 0 | bS_Exp2 -= hExp; |
90 | 0 | } |
91 | 0 |
|
92 | 0 | // Remove common power of two factor from all three scaled values |
93 | 0 | int common_Exp2 = Min3(dS_Exp2, bS_Exp2, hS_Exp2); |
94 | 0 | dS_Exp2 -= common_Exp2; |
95 | 0 | bS_Exp2 -= common_Exp2; |
96 | 0 | hS_Exp2 -= common_Exp2; |
97 | 0 |
|
98 | 0 | BigInteger dS = d; |
99 | 0 | dS.MultiplyPow5(static_cast<unsigned>(dS_Exp5)) <<= static_cast<unsigned>(dS_Exp2); |
100 | 0 |
|
101 | 0 | BigInteger bS(bInt); |
102 | 0 | bS.MultiplyPow5(static_cast<unsigned>(bS_Exp5)) <<= static_cast<unsigned>(bS_Exp2); |
103 | 0 |
|
104 | 0 | BigInteger hS(1); |
105 | 0 | hS.MultiplyPow5(static_cast<unsigned>(hS_Exp5)) <<= static_cast<unsigned>(hS_Exp2); |
106 | 0 |
|
107 | 0 | BigInteger delta(0); |
108 | 0 | dS.Difference(bS, &delta); |
109 | 0 |
|
110 | 0 | return delta.Compare(hS); |
111 | 0 | } |
112 | | |
113 | 0 | inline bool StrtodFast(double d, int p, double* result) { |
114 | 0 | // Use fast path for string-to-double conversion if possible |
115 | 0 | // see http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ |
116 | 0 | if (p > 22 && p < 22 + 16) { |
117 | 0 | // Fast Path Cases In Disguise |
118 | 0 | d *= internal::Pow10(p - 22); |
119 | 0 | p = 22; |
120 | 0 | } |
121 | 0 |
|
122 | 0 | if (p >= -22 && p <= 22 && d <= 9007199254740991.0) { // 2^53 - 1 |
123 | 0 | *result = FastPath(d, p); |
124 | 0 | return true; |
125 | 0 | } |
126 | 0 | else |
127 | 0 | return false; |
128 | 0 | } |
129 | | |
130 | | // Compute an approximation and see if it is within 1/2 ULP |
131 | | template<typename Ch> |
132 | 0 | inline bool StrtodDiyFp(const Ch* decimals, int dLen, int dExp, double* result) { |
133 | 0 | uint64_t significand = 0; |
134 | 0 | int i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999 |
135 | 0 | for (; i < dLen; i++) { |
136 | 0 | if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || |
137 | 0 | (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > Ch('5'))) |
138 | 0 | break; |
139 | 0 | significand = significand * 10u + static_cast<unsigned>(decimals[i] - Ch('0')); |
140 | 0 | } |
141 | 0 | |
142 | 0 | if (i < dLen && decimals[i] >= Ch('5')) // Rounding |
143 | 0 | significand++; |
144 | 0 |
|
145 | 0 | int remaining = dLen - i; |
146 | 0 | const int kUlpShift = 3; |
147 | 0 | const int kUlp = 1 << kUlpShift; |
148 | 0 | int64_t error = (remaining == 0) ? 0 : kUlp / 2; |
149 | 0 |
|
150 | 0 | DiyFp v(significand, 0); |
151 | 0 | v = v.Normalize(); |
152 | 0 | error <<= -v.e; |
153 | 0 |
|
154 | 0 | dExp += remaining; |
155 | 0 |
|
156 | 0 | int actualExp; |
157 | 0 | DiyFp cachedPower = GetCachedPower10(dExp, &actualExp); |
158 | 0 | if (actualExp != dExp) { |
159 | 0 | static const DiyFp kPow10[] = { |
160 | 0 | DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 0x00000000), -60), // 10^1 |
161 | 0 | DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 0x00000000), -57), // 10^2 |
162 | 0 | DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 0x00000000), -54), // 10^3 |
163 | 0 | DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), -50), // 10^4 |
164 | 0 | DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 0x00000000), -47), // 10^5 |
165 | 0 | DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 0x00000000), -44), // 10^6 |
166 | 0 | DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 0x00000000), -40) // 10^7 |
167 | 0 | }; |
168 | 0 | int adjustment = dExp - actualExp; |
169 | 0 | RAPIDJSON_ASSERT(adjustment >= 1 && adjustment < 8); |
170 | 0 | v = v * kPow10[adjustment - 1]; |
171 | 0 | if (dLen + adjustment > 19) // has more digits than decimal digits in 64-bit |
172 | 0 | error += kUlp / 2; |
173 | 0 | } |
174 | 0 |
|
175 | 0 | v = v * cachedPower; |
176 | 0 |
|
177 | 0 | error += kUlp + (error == 0 ? 0 : 1); |
178 | 0 |
|
179 | 0 | const int oldExp = v.e; |
180 | 0 | v = v.Normalize(); |
181 | 0 | error <<= oldExp - v.e; |
182 | 0 |
|
183 | 0 | const int effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); |
184 | 0 | int precisionSize = 64 - effectiveSignificandSize; |
185 | 0 | if (precisionSize + kUlpShift >= 64) { |
186 | 0 | int scaleExp = (precisionSize + kUlpShift) - 63; |
187 | 0 | v.f >>= scaleExp; |
188 | 0 | v.e += scaleExp; |
189 | 0 | error = (error >> scaleExp) + 1 + kUlp; |
190 | 0 | precisionSize -= scaleExp; |
191 | 0 | } |
192 | 0 |
|
193 | 0 | DiyFp rounded(v.f >> precisionSize, v.e + precisionSize); |
194 | 0 | const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp; |
195 | 0 | const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp; |
196 | 0 | if (precisionBits >= halfWay + static_cast<unsigned>(error)) { |
197 | 0 | rounded.f++; |
198 | 0 | if (rounded.f & (DiyFp::kDpHiddenBit << 1)) { // rounding overflows mantissa (issue #340) |
199 | 0 | rounded.f >>= 1; |
200 | 0 | rounded.e++; |
201 | 0 | } |
202 | 0 | } |
203 | 0 |
|
204 | 0 | *result = rounded.ToDouble(); |
205 | 0 |
|
206 | 0 | return halfWay - static_cast<unsigned>(error) >= precisionBits || precisionBits >= halfWay + static_cast<unsigned>(error); |
207 | 0 | } |
208 | | |
209 | | template<typename Ch> |
210 | 0 | inline double StrtodBigInteger(double approx, const Ch* decimals, int dLen, int dExp) { |
211 | 0 | RAPIDJSON_ASSERT(dLen >= 0); |
212 | 0 | const BigInteger dInt(decimals, static_cast<unsigned>(dLen)); |
213 | 0 | Double a(approx); |
214 | 0 | int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp); |
215 | 0 | if (cmp < 0) |
216 | 0 | return a.Value(); // within half ULP |
217 | 0 | else if (cmp == 0) { |
218 | 0 | // Round towards even |
219 | 0 | if (a.Significand() & 1) |
220 | 0 | return a.NextPositiveDouble(); |
221 | 0 | else |
222 | 0 | return a.Value(); |
223 | 0 | } |
224 | 0 | else // adjustment |
225 | 0 | return a.NextPositiveDouble(); |
226 | 0 | } |
227 | | |
228 | | template<typename Ch> |
229 | 0 | inline double StrtodFullPrecision(double d, int p, const Ch* decimals, size_t length, size_t decimalPosition, int exp) { |
230 | 0 | RAPIDJSON_ASSERT(d >= 0.0); |
231 | 0 | RAPIDJSON_ASSERT(length >= 1); |
232 | 0 |
|
233 | 0 | double result = 0.0; |
234 | 0 | if (StrtodFast(d, p, &result)) |
235 | 0 | return result; |
236 | 0 |
|
237 | 0 | RAPIDJSON_ASSERT(length <= INT_MAX); |
238 | 0 | int dLen = static_cast<int>(length); |
239 | 0 |
|
240 | 0 | RAPIDJSON_ASSERT(length >= decimalPosition); |
241 | 0 | RAPIDJSON_ASSERT(length - decimalPosition <= INT_MAX); |
242 | 0 | int dExpAdjust = static_cast<int>(length - decimalPosition); |
243 | 0 |
|
244 | 0 | RAPIDJSON_ASSERT(exp >= INT_MIN + dExpAdjust); |
245 | 0 | int dExp = exp - dExpAdjust; |
246 | 0 |
|
247 | 0 | // Make sure length+dExp does not overflow |
248 | 0 | RAPIDJSON_ASSERT(dExp <= INT_MAX - dLen); |
249 | 0 |
|
250 | 0 | // Trim leading zeros |
251 | 0 | while (dLen > 0 && *decimals == '0') { |
252 | 0 | dLen--; |
253 | 0 | decimals++; |
254 | 0 | } |
255 | 0 |
|
256 | 0 | // Trim trailing zeros |
257 | 0 | while (dLen > 0 && decimals[dLen - 1] == '0') { |
258 | 0 | dLen--; |
259 | 0 | dExp++; |
260 | 0 | } |
261 | 0 |
|
262 | 0 | if (dLen == 0) { // Buffer only contains zeros. |
263 | 0 | return 0.0; |
264 | 0 | } |
265 | 0 |
|
266 | 0 | // Trim right-most digits |
267 | 0 | const int kMaxDecimalDigit = 767 + 1; |
268 | 0 | if (dLen > kMaxDecimalDigit) { |
269 | 0 | dExp += dLen - kMaxDecimalDigit; |
270 | 0 | dLen = kMaxDecimalDigit; |
271 | 0 | } |
272 | 0 |
|
273 | 0 | // If too small, underflow to zero. |
274 | 0 | // Any x <= 10^-324 is interpreted as zero. |
275 | 0 | if (dLen + dExp <= -324) |
276 | 0 | return 0.0; |
277 | 0 |
|
278 | 0 | // If too large, overflow to infinity. |
279 | 0 | // Any x >= 10^309 is interpreted as +infinity. |
280 | 0 | if (dLen + dExp > 309) |
281 | 0 | return std::numeric_limits<double>::infinity(); |
282 | 0 |
|
283 | 0 | if (StrtodDiyFp(decimals, dLen, dExp, &result)) |
284 | 0 | return result; |
285 | 0 |
|
286 | 0 | // Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison |
287 | 0 | return StrtodBigInteger(result, decimals, dLen, dExp); |
288 | 0 | } |
289 | | |
290 | | } // namespace internal |
291 | | RAPIDJSON_NAMESPACE_END |
292 | | |
293 | | #endif // RAPIDJSON_STRTOD_ |