Coverage Report

Created: 2026-10-28 07:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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_