1/*
2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "ecmascript/base/number_helper.h"
17
18#include <cfenv>
19#include <cmath>
20#include <cstddef>
21#include <cstdint>
22#include <iomanip>
23#include <sstream>
24#include <sys/time.h>
25
26#include "ecmascript/base/dtoa_helper.h"
27#include "ecmascript/base/string_helper.h"
28#include "ecmascript/builtins/builtins_number.h"
29#include "ecmascript/ecma_string_table.h"
30#include "ecmascript/js_tagged_value-inl.h"
31
32namespace panda::ecmascript::base {
33using NumberToStringResultCache = builtins::NumberToStringResultCache;
34
35enum class Sign { NONE, NEG, POS };
36thread_local uint64_t RandomGenerator::randomState_ {0};
37// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
38#define RETURN_IF_CONVERSION_END(p, end, result) \
39    if ((p) == (end)) {                          \
40        return (result);                         \
41    }
42
43constexpr char CHARS[] = "0123456789abcdefghijklmnopqrstuvwxyz";  // NOLINT (modernize-avoid-c-arrays)
44constexpr uint64_t MAX_MANTISSA = 0x1ULL << 52U;
45
46static const double POWERS_OF_TEN[] = {
47    1.0,                      // 10^0
48    10.0,
49    100.0,
50    1000.0,
51    10000.0,
52    100000.0,
53    1000000.0,
54    10000000.0,
55    100000000.0,
56    1000000000.0,
57    10000000000.0,            // 10^10
58    100000000000.0,
59    1000000000000.0,
60    10000000000000.0,
61    100000000000000.0,
62    1000000000000000.0,
63    10000000000000000.0,
64    100000000000000000.0,
65    1000000000000000000.0,
66    10000000000000000000.0,
67    100000000000000000000.0,  // 10^20
68    1000000000000000000000.0,
69    10000000000000000000000.0 // 10^22
70};
71static const int POWERS_OF_TEN_SIZE = 23;
72
73static inline uint8_t ToDigit(uint8_t c)
74{
75    if (c >= '0' && c <= '9') {
76        return c - '0';
77    }
78    if (c >= 'A' && c <= 'Z') {
79        return c - 'A' + DECIMAL;
80    }
81    if (c >= 'a' && c <= 'z') {
82        return c - 'a' + DECIMAL;
83    }
84    return '$';
85}
86
87bool NumberHelper::GotoNonspace(uint8_t **ptr, const uint8_t *end)
88{
89    while (*ptr < end) {
90        uint16_t c = **ptr;
91        size_t size = 1;
92        if (c > INT8_MAX) {
93            size = 0;
94            uint16_t utf8Bit = INT8_MAX + 1;  // equal 0b1000'0000
95            while (utf8Bit > 0 && (c & utf8Bit) == utf8Bit) {
96                ++size;
97                utf8Bit >>= 1UL;
98            }
99            if (base::utf_helper::ConvertRegionUtf8ToUtf16(*ptr, &c, end - *ptr, 1) <= 0) {
100                return true;
101            }
102        }
103        if (!StringHelper::IsNonspace(c)) {
104            return true;
105        }
106        *ptr += size;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
107    }
108    return false;
109}
110
111static inline double SignedZero(Sign sign)
112{
113    return sign == Sign::NEG ? -0.0 : 0.0;
114}
115
116bool NumberHelper::IsEmptyString(const uint8_t *start, const uint8_t *end)
117{
118    auto p = const_cast<uint8_t *>(start);
119    return !NumberHelper::GotoNonspace(&p, end);
120}
121
122/*
123*  This Function Translate from number 0-9 to number '0'-'9'
124*                               number 10-35 to number 'a'-'z'
125*/
126uint32_t NumberHelper::ToCharCode(uint32_t number)
127{
128    ASSERT(number < 36); // 36 == total number of '0'-'9' + 'a' -'z'
129    return number < 10 ? (number + 48): // 48 == '0'; 10: '0' - '9';
130                         (number - 10 + 97); // 97 == 'a'; 'a' - 'z'
131}
132
133JSTaggedValue NumberHelper::Int32ToString(JSThread *thread, int32_t number, uint32_t radix)
134{
135    bool isNegative = number < 0;
136    uint32_t n = 0;
137    if (!isNegative) {
138        n = static_cast<uint32_t>(number);
139        if (n < radix) {
140            if (n == 0) {
141                return thread->GlobalConstants()->GetHandledZeroString().GetTaggedValue();
142            }
143            JSHandle<SingleCharTable> singleCharTable(thread, thread->GetSingleCharTable());
144            return singleCharTable->GetStringFromSingleCharTable(ToCharCode(n));
145        }
146    } else {
147        n = static_cast<uint32_t>(-number);
148    }
149    uint32_t temp = n;
150    uint32_t length = isNegative ? 1 : 0;
151    // calculate length
152    while (temp > 0) {
153        temp = temp / radix;
154        length = length + 1;
155    }
156    std::string buf;
157    buf.resize(length);
158    ASSERT(length > 0);
159    uint32_t index = length - 1;
160    uint32_t digit = 0;
161    while (n > 0) {
162        digit = n % radix;
163        n /= radix;
164        buf[index] = ToCharCode(digit) + 0X00;
165        index--;
166    }
167    if (isNegative) {
168        ASSERT(index == 0);
169        buf[index] = '-';
170    }
171    return thread->GetEcmaVM()->GetFactory()->NewFromUtf8(buf).GetTaggedValue();
172}
173
174JSTaggedValue NumberHelper::DoubleToString(JSThread *thread, double number, int radix)
175{
176    static constexpr int BUFFER_SIZE = 2240; // 2240: The size of the character array buffer
177    static constexpr int HALF_BUFFER_SIZE = BUFFER_SIZE >> 1;
178    char buffer[BUFFER_SIZE];
179    size_t integerCursor = HALF_BUFFER_SIZE;
180    size_t fractionCursor = integerCursor;
181
182    bool negative = number < 0.0;
183    if (negative) {
184        number = -number;
185    }
186
187    double integer = std::floor(number);
188    double fraction = number - integer;
189
190    auto value = bit_cast<uint64_t>(number);
191    value += 1;
192    double delta = HALF * (bit_cast<double>(value) - number);
193    delta = std::max(delta, bit_cast<double>(static_cast<uint64_t>(1))); // 1 : The binary of the smallest double is 1
194    if (fraction != 0 && fraction >= delta) {
195        buffer[fractionCursor++] = '.';
196        while (fraction >= delta) {
197            fraction *= radix;
198            delta *= radix;
199            int64_t digit = std::floor(fraction);
200            fraction -= digit;
201            buffer[fractionCursor++] = CHARS[digit];
202            bool needCarry = (fraction > HALF) && (fraction + delta > 1);
203            if (needCarry) {
204                size_t fractionEnd = fractionCursor - 1;
205                buffer[fractionEnd] = Carry(buffer[fractionEnd], radix);
206                for (; fractionEnd > HALF_BUFFER_SIZE; fractionEnd--) {
207                    if (buffer[fractionEnd] == '0') {
208                        buffer[fractionEnd - 1] = Carry(buffer[fractionEnd - 1], radix);
209                    } else {
210                        break;
211                    }
212                }
213                if (fractionEnd == HALF_BUFFER_SIZE) {
214                    ++integer;
215                }
216                break;
217            }
218        }
219        // delete 0 in the end
220        size_t fractionEnd = fractionCursor - 1;
221        while (buffer[fractionEnd] == '0') {
222            --fractionEnd;
223        }
224        fractionCursor = fractionEnd + 1;
225    }
226
227    ASSERT(radix >= MIN_RADIX && radix <= MAX_RADIX);
228    while (Exponent(integer / radix) > 0) {
229        integer /= radix;
230        buffer[--integerCursor] = '0';
231    }
232    do {
233        double remainder = std::fmod(integer, radix);
234        buffer[--integerCursor] = CHARS[static_cast<int>(remainder)];
235        integer = (integer - remainder) / radix;
236    } while (integer > 0);
237
238    if (negative) {
239        buffer[--integerCursor] = '-';
240    }
241    buffer[fractionCursor++] = '\0';
242
243    size_t size = fractionCursor - integerCursor;
244    std::unique_ptr<char[]> result = std::make_unique<char[]>(size);
245    if (memcpy_s(result.get(), size, buffer + integerCursor, size) != EOK) {
246        LOG_FULL(FATAL) << "memcpy_s failed";
247        UNREACHABLE();
248    }
249    return BuiltinsBase::GetTaggedString(thread, result.get());
250}
251
252JSTaggedValue NumberHelper::DoubleToFixedString(JSThread *thread, double valueNumber, int digitNumber)
253{
254    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
255    bool negative = false;
256    double absValue = valueNumber;
257    std::string result;
258
259    if (valueNumber < 0) {
260        result += "-";
261        absValue = -valueNumber;
262        negative = true;
263    }
264    int decimalPoint;
265    const int decimalRepCapacity = MAX_DIGITS + MAX_FRACTION + 1; // Add space for the '\0' byte.
266    char decimalRep[decimalRepCapacity];
267    int length;
268    bool isFast = DtoaHelper::FixedDtoa(absValue, digitNumber,
269        BufferVector<char>(decimalRep, decimalRepCapacity), &length, &decimalPoint);
270    if (!isFast) {
271        return DoubleToASCII(thread, valueNumber, digitNumber, base::FRAC_FORMAT); // slow
272    }
273    int zeroPrefixLen = 0;
274    int zeroPostfixLen = 0;
275    if (decimalPoint <= 0) {
276        zeroPrefixLen = -decimalPoint + 1;
277        decimalPoint = 1;
278    }
279    if (zeroPrefixLen + length < decimalPoint + digitNumber) {
280        zeroPostfixLen = decimalPoint + digitNumber - length - zeroPrefixLen;
281    }
282    result += std::string(zeroPrefixLen, '0');
283    result += decimalRep;
284    result += std::string(zeroPostfixLen, '0');
285    if (digitNumber > 0) {
286        if (negative) {
287            result.insert(decimalPoint + 1, 1, '.');
288        } else {
289            result.insert(decimalPoint, 1, '.');
290        }
291    }
292    return factory->NewFromASCII(result.c_str()).GetTaggedValue();
293}
294
295JSTaggedValue NumberHelper::DoubleToASCII(JSThread *thread, double valueNumber, int digitNumber, int flags)
296{
297    std::string buffer(JS_DTOA_BUF_SIZE, '\0');
298    DoubleToASCIIWithFlag(buffer, valueNumber, digitNumber, flags);
299    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
300    return factory->NewFromASCII(buffer.c_str()).GetTaggedValue();
301}
302
303void NumberHelper::GetBaseForRoundingMode(double valueNumber, int digitNumber, int *decimalPoint, std::string& buf,
304                                          std::string& buf1, int buf1Size, int roundingMode, int *sign)
305{
306    if (roundingMode != FE_TONEAREST) {
307        fesetround(roundingMode);
308    }
309    int result = snprintf_s(&buf1[0], buf1Size, buf1Size - 1, "%+.*e", digitNumber - 1, valueNumber);
310    if (result == -1) {
311        LOG_FULL(FATAL) << "snprintf_s failed";
312        UNREACHABLE();
313    }
314    if (roundingMode != FE_TONEAREST) {
315        fesetround(FE_TONEAREST);
316    }
317    *sign = (buf1[0] == '-');
318    buf[0] = buf1[1];
319    if (digitNumber > 1) {
320        if (memcpy_s(&buf[1], digitNumber - 1, &buf1[POINT_INDEX], digitNumber - 1) != EOK) {
321            LOG_FULL(FATAL) << "memcpy_s failed";
322            UNREACHABLE();
323        }
324    }
325    buf[digitNumber] = '\0';
326    *decimalPoint = std::atoi(&buf1[digitNumber + DECIMAL_INDEX + (digitNumber > 1)]) + 1;
327}
328
329void NumberHelper::CustomEcvtIsFixed(double &valueNumber, int &digits, int *decimalPoint, std::string& buf, int *sign)
330{
331    std::string buffer(JS_DTOA_BUF_SIZE, '\0');
332    unsigned int digitsMin = 1;
333    unsigned int digitsMax = DOUBLE_MAX_PRECISION;
334    while (digitsMin < digitsMax) {
335        digits = (digitsMin + digitsMax) / MIN_RADIX;
336        ASSERT(buffer.size() <= JS_DTOA_BUF_SIZE);
337        GetBaseForRoundingMode(valueNumber, digits, decimalPoint, buf, buffer, JS_DTOA_BUF_SIZE, FE_TONEAREST, sign);
338        if (std::strtod(buffer.c_str(), NULL) == valueNumber) {
339            while (digits >= MIN_RADIX && buf[digits - 1] == '0') {
340                digits--;
341            }
342            digitsMax = static_cast<unsigned int>(digits);
343        } else {
344            digitsMin = static_cast<unsigned int>(digits) + 1;
345        }
346    }
347    digits = static_cast<int>(digitsMax);
348}
349
350int NumberHelper::CustomEcvt(double valueNumber, int digits, int *decimalPoint,
351                             std::string& buf, bool isFixed, int *sign)
352{
353    std::string buffer(JS_DTOA_BUF_SIZE, '\0');
354    int roundingMode = FE_TONEAREST;
355    if (!isFixed) {
356        CustomEcvtIsFixed(valueNumber, digits, decimalPoint, buf, sign);
357    } else {
358        std::string buf1(JS_DTOA_BUF_SIZE, '\0');
359        std::string buf2(JS_DTOA_BUF_SIZE, '\0');
360        int decpt1 = 0;
361        int decpt2 = 0;
362        int sign1 = 0;
363        int sign2 = 0;
364        ASSERT(buffer.size() <= JS_DTOA_BUF_SIZE);
365        GetBaseForRoundingMode(valueNumber, digits + 1, &decpt1, buf1, buffer, JS_DTOA_BUF_SIZE, roundingMode, &sign1);
366        if (buf1[digits] == HALFCHAR) {
367            ASSERT(buf1.size() <= JS_DTOA_BUF_SIZE);
368            GetBaseForRoundingMode(valueNumber, digits + 1, &decpt1, buf1, buffer, JS_DTOA_BUF_SIZE,
369                FE_DOWNWARD, &sign1);
370            ASSERT(buf2.size() <= JS_DTOA_BUF_SIZE);
371            GetBaseForRoundingMode(valueNumber, digits + 1, &decpt2, buf2, buffer, JS_DTOA_BUF_SIZE,
372                FE_UPWARD, &sign2);
373            if (memcmp(buf1.c_str(), buf2.c_str(), digits + 1) == 0 && decpt1 == decpt2) {
374                roundingMode = sign1 ? FE_DOWNWARD : FE_UPWARD;
375            }
376        }
377    }
378    ASSERT(buffer.size() <= JS_DTOA_BUF_SIZE);
379    GetBaseForRoundingMode(valueNumber, digits, decimalPoint, buf, buffer, JS_DTOA_BUF_SIZE, roundingMode, sign);
380    return digits;
381}
382
383int NumberHelper::CustomFcvtHelper(std::string& buf, int bufSize, double valueNumber, int digits, int roundingMode)
384{
385    if (roundingMode != FE_TONEAREST) {
386        std::fesetround(roundingMode);
387    }
388    int result = snprintf_s(&buf[0], bufSize, bufSize, "%.*f", digits, valueNumber);
389    if (result == -1) {
390        LOG_FULL(FATAL) << "snprintf_s failed";
391        UNREACHABLE();
392    }
393    if (roundingMode != FE_TONEAREST) {
394        std::fesetround(FE_TONEAREST);
395    }
396    ASSERT(result < bufSize);
397    return result;
398}
399
400void NumberHelper::CustomFcvt(std::string& buf, int bufSize, double valueNumber, int digits)
401{
402    int number = 0;
403    int tmpNumber = 0;
404    std::string tmpbuf1(JS_DTOA_BUF_SIZE, '\0');
405    std::string tmpbuf2(JS_DTOA_BUF_SIZE, '\0');
406    int roundingMode = FE_TONEAREST;
407    number = CustomFcvtHelper(tmpbuf1, JS_DTOA_BUF_SIZE, valueNumber, digits + 1, roundingMode);
408    if (tmpbuf1[number - 1] == HALFCHAR) {
409        number = CustomFcvtHelper(tmpbuf1, JS_DTOA_BUF_SIZE, valueNumber, digits + 1, FE_DOWNWARD);
410        tmpNumber = CustomFcvtHelper(tmpbuf2, JS_DTOA_BUF_SIZE, valueNumber, digits + 1, FE_UPWARD);
411        if (tmpbuf1 == tmpbuf2) {
412            if (tmpbuf1[0] == '-') {
413                roundingMode = FE_DOWNWARD;
414            } else {
415                roundingMode = FE_UPWARD;
416            }
417        }
418    }
419    CustomFcvtHelper(buf, bufSize, valueNumber, digits, roundingMode);
420}
421
422JSTaggedValue NumberHelper::DoubleToPrecisionString(JSThread *thread, double number, int digit)
423{
424    if (number == 0.0) {
425        return DoubleToFixedString(thread, number, digit - 1);
426    }
427    double positiveNumber = number > 0 ? number : -number;
428    int logDigit = std::floor(log10(positiveNumber));
429    int radixDigit = digit - logDigit - 1;
430    const int MIN_EXPONENT_DIGIT = -6;
431    if ((logDigit >= MIN_EXPONENT_DIGIT && logDigit < digit)) {
432        return DoubleToFixedString(thread, number, std::abs(radixDigit));
433    }
434    return DoubleToExponential(thread, number, digit);
435}
436
437JSTaggedValue NumberHelper::DoubleToExponential(JSThread *thread, double number, int digit)
438{
439    char tmpbuf[JS_DTOA_BUF_SIZE] = {0};
440    // Can use std::to_chars for performance.
441    if (digit == 0) {
442        if (number == 0.0) {
443            return BuiltinsBase::GetTaggedString(thread, "0e+0");
444        }
445        std::string res;
446        if (number < 0) {
447            res += "-";
448            number = -number;
449        }
450        int n;
451        int k;
452        DtoaHelper::Dtoa(number, tmpbuf, &n, &k);
453        std::string base = tmpbuf;
454        base.erase(1, k - 1);
455        if (k != 1) {
456            base += std::string(".") + std::string(tmpbuf + 1);
457        }
458        base += "e" + (n >= 1 ? std::string("+") : "") + std::to_string(n - 1);
459        res += base;
460        return BuiltinsBase::GetTaggedString(thread, res.c_str());
461    } else {
462        int result = snprintf_s(tmpbuf, sizeof(tmpbuf), sizeof(tmpbuf) - 1, "%.*e", digit - 1, number);
463        if (result == -1) {
464            LOG_FULL(FATAL) << "snprintf_s failed";
465            UNREACHABLE();
466        }
467    }
468    std::string result = tmpbuf;
469    size_t found = result.find_last_of('e');
470    if (found != CString::npos && found < result.size() - 2 && result[found + 2] == '0') { // 2:offset of e
471        result.erase(found + 2, 1); // 2:offset of e
472    }
473    if (digit < 0) {
474        size_t end = found;
475        while (--found > 0) {
476            if (result[found] != '0') {
477                break;
478            }
479        }
480        if (result[found] == '.') {
481            found--;
482        }
483        if (found < end - 1) {
484            result.erase(found + 1, end - found - 1);
485        }
486    }
487    return BuiltinsBase::GetTaggedString(thread, result.c_str());
488}
489
490void NumberHelper::DoubleToASCIIWithFlag(std::string& buf, double valueNumber, int digits, int flags)
491{
492    if (valueNumber == 0.0) {
493        valueNumber = 0.0;
494    }
495    if (flags == FRAC_FORMAT) {
496        CustomFcvt(buf, JS_DTOA_BUF_SIZE, valueNumber, digits);
497    } else {
498        std::string buf1(JS_DTOA_BUF_SIZE, '\0');
499        int decimalPoint = 0;
500        int sign = 0;
501        bool fixed = ((static_cast<unsigned int>(flags) & POINT_INDEX) ==
502            static_cast<unsigned int>(base::FIXED_FORMAT));
503        int numberMax = fixed ? digits : MAX_DIGITS;
504        int digitNumber = CustomEcvt(valueNumber, digits, &decimalPoint, buf1, fixed, &sign);
505        int number = decimalPoint;
506        std::string tmpbuf;
507        int i = 0;
508        if (sign) {
509            tmpbuf += '-';
510        }
511        if (number > 0 && number <= numberMax) {
512            ToASCIIWithGreatThanZero(tmpbuf, digitNumber, number, buf1);
513        } else if (MIN_DIGITS < number && number <= 0) {
514            tmpbuf += '0';
515            tmpbuf += '.';
516            for (i = 0; i < -number; i++) {
517                tmpbuf += '0';
518            }
519            tmpbuf += buf1.substr(0, digitNumber);
520        } else {
521            ToASCIIWithNegative(tmpbuf, digitNumber, number, buf1);
522        }
523        buf = tmpbuf;
524    }
525}
526
527void NumberHelper::ToASCIIWithGreatThanZero(std::string& tmpbuf, int digitNumber, int number, const std::string& buf)
528{
529    if (digitNumber <= number) {
530        tmpbuf += buf.substr(0, digitNumber);
531        tmpbuf += std::string(number - digitNumber, '0');
532        tmpbuf += '\0';
533    } else {
534        tmpbuf += buf.substr(0, number);
535        tmpbuf += '.';
536        tmpbuf += buf.substr(number, digitNumber - number);
537        tmpbuf += '\0';
538    }
539}
540
541void NumberHelper::ToASCIIWithNegative(std::string& tmpbuf, int digitNumber, int n, const std::string& buf)
542{
543    tmpbuf += buf[0];
544    if (digitNumber > 1) {
545        tmpbuf += '.';
546        for (int i = 1; i < digitNumber; i++) {
547            tmpbuf += buf[i];
548        }
549    }
550    tmpbuf += 'e';
551    int p = n - 1;
552    if (p >= 0) {
553        tmpbuf += '+';
554    }
555    tmpbuf += std::to_string(p);
556}
557
558JSTaggedValue NumberHelper::StringToNumber(EcmaString *string, int32_t radix)
559{
560    bool negative = false;
561    if ((radix == base::DECIMAL || radix == 0)) {
562        int32_t elementIndex = 0;
563        if (EcmaStringAccessor(string).ToInt(&elementIndex, &negative)) {
564            if (elementIndex == 0 && negative == true) {
565                return JSTaggedValue(-0.0);
566            }
567            return JSTaggedValue(elementIndex);
568        }
569    }
570    CVector<uint8_t> buf;
571    Span<const uint8_t> str = EcmaStringAccessor(string).ToUtf8Span(buf);
572
573    JSTaggedValue result = NumberHelper::StringToDoubleWithRadix(str.begin(), str.end(), radix, &negative);
574    if (result.GetNumber() == 0 && negative == true) {
575        return JSTaggedValue(-0.0);
576    }
577    return JSTaggedValue::TryCastDoubleToInt32(result.GetNumber());
578}
579
580JSTaggedValue NumberHelper::StringToDoubleWithRadix(const uint8_t *start, const uint8_t *end, int radix, bool *negative)
581{
582    auto p = const_cast<uint8_t *>(start);
583    JSTaggedValue nanResult = BuiltinsBase::GetTaggedDouble(NAN_VALUE);
584    // 1. skip space and line terminal
585    if (!NumberHelper::GotoNonspace(&p, end)) {
586        return nanResult;
587    }
588
589    // 2. sign bit
590    if (*p == '-') {
591        *negative = true;
592        RETURN_IF_CONVERSION_END(++p, end, nanResult);
593    } else if (*p == '+') {
594        RETURN_IF_CONVERSION_END(++p, end, nanResult);
595    }
596    // 3. 0x or 0X
597    bool stripPrefix = true;
598    // 4. If R  0, then
599    //     a. If R < 2 or R > 36, return NaN.
600    //     b. If R  16, let stripPrefix be false.
601    if (radix != 0) {
602        if (radix < MIN_RADIX || radix > MAX_RADIX) {
603            return nanResult;
604        }
605        if (radix != HEXADECIMAL) {
606            stripPrefix = false;
607        }
608    } else {
609        radix = DECIMAL;
610    }
611    int size = 0;
612    if (stripPrefix) {
613        if (*p == '0') {
614            if (++p != end && (*p == 'x' || *p == 'X')) {
615                RETURN_IF_CONVERSION_END(++p, end, nanResult);
616                radix = HEXADECIMAL;
617            } else {
618                size++;
619            }
620        }
621    }
622
623    double result = 0;
624    bool isDone = false;
625    do {
626        double part = 0;
627        uint32_t multiplier = 1;
628        for (; p != end; ++p) {
629            // The maximum value to ensure that uint32_t will not overflow
630            const uint32_t MAX_MULTIPER = 0xffffffffU / 36;
631            uint32_t m = multiplier * static_cast<uint32_t>(radix);
632            if (m > MAX_MULTIPER) {
633                break;
634            }
635
636            int currentBit = static_cast<int>(ToDigit(*p));
637            if (currentBit >= radix) {
638                isDone = true;
639                break;
640            }
641            size++;
642            part = part * radix + currentBit;
643            multiplier = m;
644        }
645        result = result * multiplier + part;
646        if (isDone) {
647            break;
648        }
649    } while (p != end);
650
651    if (size == 0) {
652        return nanResult;
653    }
654
655    if (*negative) {
656        result = -result;
657    }
658    return BuiltinsBase::GetTaggedDouble(result);
659}
660
661char NumberHelper::Carry(char current, int radix)
662{
663    int digit = static_cast<int>((current > '9') ? (current - 'a' + DECIMAL) : (current - '0'));
664    digit = (digit == (radix - 1)) ? 0 : digit + 1;
665    return CHARS[digit];
666}
667
668CString NumberHelper::IntegerToString(double number, int radix)
669{
670    ASSERT(radix >= MIN_RADIX && radix <= MAX_RADIX);
671    CString result;
672    while (number / radix > MAX_MANTISSA) {
673        number /= radix;
674        result = CString("0").append(result);
675    }
676    do {
677        double remainder = std::fmod(number, radix);
678        result = CHARS[static_cast<int>(remainder)] + result;
679        number = (number - remainder) / radix;
680    } while (number > 0);
681    return result;
682}
683
684CString NumberHelper::IntToString(int number)
685{
686    return ToCString(number);
687}
688
689JSHandle<EcmaString> NumberHelper::IntToEcmaString(const JSThread *thread, int number)
690{
691    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
692    return factory->NewFromASCII(ToCString(number));
693}
694
695JSHandle<EcmaString> NumberHelper::DoubleToEcmaString(const JSThread *thread, double d)
696{
697    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
698    if (std::isnan(d)) {
699        return JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledNanCapitalString());
700    }
701    if (d == 0.0) {
702        return JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledZeroString());
703    }
704    if (d >= INT32_MIN + 1 && d <= INT32_MAX && d == static_cast<double>(static_cast<int32_t>(d))) {
705        return factory->NewFromASCII(IntToString(static_cast<int32_t>(d)));
706    }
707
708    if (std::isinf(d)) {
709        return d < 0 ? JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledMinusInfinityCapitalString())
710                     : JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledInfinityCapitalString());
711    }
712
713    char buffer[JS_DTOA_BUF_SIZE] = {0};
714    bool isNeg = false;
715
716    if (d < 0) {
717        isNeg = true;
718        d = -d;
719    }
720    ASSERT(d > 0);
721    constexpr int startIdx = 8;
722    char *result = buffer + startIdx;
723    int n; // decimal point
724    int k; // length
725    DtoaHelper::Dtoa(d, result, &n, &k); //Fast Double To Ascii.
726    if (n > 0 && n <= MAX_DIGITS) {
727        if (k <= n) {
728            // 6. If k ≤ n ≤ 21
729            for (int i = k; i < n; ++i) {
730                result[i] = '0';
731            }
732        } else {
733            // 7. If 0 < n ≤ 21
734            --result;
735            for (int i = 0; i < n; ++i) {
736                result[i] = result[i + 1];
737            }
738            result[n] = '.';
739        }
740    } else if (MIN_DIGITS < n && n <= 0) {
741        // 8. If −6 < n ≤ 0
742        constexpr int prefixLen = 2;
743        result -= (-n + prefixLen);
744        result[0] = '0';
745        result[1] = '.';
746        for (int i = prefixLen; i < -n + prefixLen; ++i) {
747            result[i] = '0';
748        }
749    } else {
750        // 9. & 10. Otherwise
751        int pos = k;
752        if (k != 1) {
753            --result;
754            result[0] = result[1];
755            result[1] = '.';
756            ++pos;
757        }
758        result[pos++] = 'e';
759        if (n >= 1) {
760            result[pos++] = '+';
761        }
762        auto expo = std::to_string(n - 1);
763        auto p = expo.c_str();
764        for (size_t i = 0; i < expo.length(); ++i) {
765            result[pos++] = p[i];
766        }
767    }
768    if (isNeg) {
769        --result;
770        result[0] = '-';
771    }
772    return factory->NewFromASCIISkippingStringTable(result);
773}
774
775// 7.1.12.1 ToString Applied to the Number Type
776JSHandle<EcmaString> NumberHelper::NumberToString(const JSThread *thread, JSTaggedValue number)
777{
778    ASSERT(number.IsNumber());
779    JSHandle<NumberToStringResultCache> cacheTable(
780        thread->GetCurrentEcmaContext()->GetNumberToStringResultCache());
781    int entry = cacheTable->GetNumberHash(number);
782    JSTaggedValue cacheResult = cacheTable->FindCachedResult(entry, number);
783    if (cacheResult != JSTaggedValue::Undefined()) {
784        return JSHandle<EcmaString>::Cast(JSHandle<JSTaggedValue>(thread, cacheResult));
785    }
786
787    JSHandle<EcmaString> resultJSHandle;
788    if (number.IsInt()) {
789        int intVal = number.GetInt();
790        if (intVal == 0) {
791            resultJSHandle = JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledZeroString());
792        } else {
793            resultJSHandle = IntToEcmaString(thread, intVal);
794        }
795    } else {
796        resultJSHandle = DoubleToEcmaString(thread, number.GetDouble());
797    }
798
799    cacheTable->SetCachedResult(thread, entry, number, resultJSHandle);
800    return resultJSHandle;
801}
802
803double NumberHelper::TruncateDouble(double d)
804{
805    if (std::isnan(d)) {
806        return 0;
807    }
808    if (!std::isfinite(d)) {
809        return d;
810    }
811    // -0 to +0
812    if (d == 0.0) {
813        return 0;
814    }
815    double ret = (d >= 0) ? std::floor(d) : std::ceil(d);
816    if (ret == 0.0) {
817        ret = 0;
818    }
819    return ret;
820}
821
822int64_t NumberHelper::DoubleToInt64(double d)
823{
824    if (d >= static_cast<double>(std::numeric_limits<int64_t>::max())) {
825        return std::numeric_limits<int64_t>::max();
826    }
827    if (d <= static_cast<double>(std::numeric_limits<int64_t>::min())) {
828        return std::numeric_limits<int64_t>::min();
829    }
830    return static_cast<int64_t>(d);
831}
832
833bool NumberHelper::IsDigitalString(const uint8_t *start, const uint8_t *end)
834{
835    int len = end - start;
836    for (int i = 0; i < len; i++) {
837        if (*(start + i) < '0' || *(start + i) > '9') {
838            return false;
839        }
840    }
841    return true;
842}
843
844int NumberHelper::StringToInt(const uint8_t *start, const uint8_t *end)
845{
846    int num = *start - '0';
847    for (int i = 1; i < (end - start); i++) {
848        num = 10 * num + (*(start + i) - '0'); // 10 : 10 represents the base of the decimal system
849    }
850    return num;
851}
852
853// only for string is ordinary string and using UTF8 encoding
854// Fast path for short integer and some special value
855std::pair<bool, JSTaggedNumber> NumberHelper::FastStringToNumber(const uint8_t *start,
856    const uint8_t *end, JSTaggedValue string)
857{
858    ASSERT(start < end);
859    EcmaStringAccessor strAccessor(string);
860    bool minus = (start[0] == '-');
861    int pos = (minus ? 1 : 0);
862
863    if (pos == (end - start)) {
864        return {true, JSTaggedNumber(NAN_VALUE)};
865    } else if (*(start + pos) > '9') {
866        // valid number's codes not longer than '9', except 'I' and non-breaking space.
867        if (*(start + pos) != 'I' && *(start + pos) != 0xA0) {
868            return {true, JSTaggedNumber(NAN_VALUE)};
869        }
870    } else if ((end - (start + pos)) <= MAX_ELEMENT_INDEX_LEN && IsDigitalString((start + pos), end)) {
871        int num = StringToInt((start + pos), end);
872        if (minus) {
873            if (num == 0) {
874                return {true, JSTaggedNumber(SignedZero(Sign::NEG))};
875            }
876            num = -num;
877        } else {
878            if ((num != 0) || (end - start == 1)) {
879                strAccessor.TryToSetIntegerHash(num);
880            }
881        }
882        return {true, JSTaggedNumber(num)};
883    }
884
885    return {false, JSTaggedNumber(NAN_VALUE)};
886}
887
888double NumberHelper::StringToDouble(const uint8_t *start, const uint8_t *end, uint8_t radix, uint32_t flags)
889{
890    auto p = const_cast<uint8_t *>(start);
891    // 1. skip space and line terminal
892    if (!NumberHelper::GotoNonspace(&p, end)) {
893        return 0.0;
894    }
895
896    // 2. get number sign
897    Sign sign = Sign::NONE;
898    if (*p == '+') {
899        RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
900        sign = Sign::POS;
901    } else if (*p == '-') {
902        RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
903        sign = Sign::NEG;
904    }
905    bool ignoreTrailing = (flags & IGNORE_TRAILING) != 0;
906
907    // 3. judge Infinity
908    static const char INF[] = "Infinity";  // NOLINT(modernize-avoid-c-arrays)
909    if (*p == INF[0]) {
910        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
911        for (const char *i = &INF[1]; *i != '\0'; ++i) {
912            if (++p == end || *p != *i) {
913                return NAN_VALUE;
914            }
915        }
916        ++p;
917        if (!ignoreTrailing && NumberHelper::GotoNonspace(&p, end)) {
918            return NAN_VALUE;
919        }
920        return sign == Sign::NEG ? -POSITIVE_INFINITY : POSITIVE_INFINITY;
921    }
922
923    // 4. get number radix
924    bool leadingZero = false;
925    bool prefixRadix = false;
926    if (*p == '0' && radix == 0) {
927        RETURN_IF_CONVERSION_END(++p, end, SignedZero(sign));
928        if (*p == 'x' || *p == 'X') {
929            if ((flags & ALLOW_HEX) == 0) {
930                return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
931            }
932            RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
933            if (sign != Sign::NONE) {
934                return NAN_VALUE;
935            }
936            prefixRadix = true;
937            radix = HEXADECIMAL;
938        } else if (*p == 'o' || *p == 'O') {
939            if ((flags & ALLOW_OCTAL) == 0) {
940                return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
941            }
942            RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
943            if (sign != Sign::NONE) {
944                return NAN_VALUE;
945            }
946            prefixRadix = true;
947            radix = OCTAL;
948        } else if (*p == 'b' || *p == 'B') {
949            if ((flags & ALLOW_BINARY) == 0) {
950                return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
951            }
952            RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
953            if (sign != Sign::NONE) {
954                return NAN_VALUE;
955            }
956            prefixRadix = true;
957            radix = BINARY;
958        } else {
959            leadingZero = true;
960        }
961    }
962
963    if (radix == 0) {
964        radix = DECIMAL;
965    }
966    auto pStart = p;
967    // 5. skip leading '0'
968    while (*p == '0') {
969        RETURN_IF_CONVERSION_END(++p, end, SignedZero(sign));
970        leadingZero = true;
971    }
972    // 6. parse to number
973    uint64_t intNumber = 0;
974    uint64_t numberMax = (UINT64_MAX - (radix - 1)) / radix;
975    int digits = 0;
976    int exponent = 0;
977    do {
978        uint8_t c = ToDigit(*p);
979        if (c >= radix) {
980            if (!prefixRadix || ignoreTrailing || (pStart != p && !NumberHelper::GotoNonspace(&p, end))) {
981                break;
982            }
983            // "0b" "0x1.2" "0b1e2" ...
984            return NAN_VALUE;
985        }
986        ++digits;
987        if (intNumber < numberMax) {
988            intNumber = intNumber * radix + c;
989        } else {
990            ++exponent;
991        }
992    } while (++p != end);
993
994    auto number = static_cast<double>(intNumber);
995    if (sign == Sign::NEG) {
996        if (number == 0) {
997            number = -0.0;
998        } else {
999            number = -number;
1000        }
1001    }
1002
1003    // 7. deal with other radix except DECIMAL
1004    if (p == end || radix != DECIMAL) {
1005        if ((digits == 0 && !leadingZero) || (p != end && !ignoreTrailing && NumberHelper::GotoNonspace(&p, end))) {
1006            // no digits there, like "0x", "0xh", or error trailing of "0x3q"
1007            return NAN_VALUE;
1008        }
1009        return number * std::pow(radix, exponent);
1010    }
1011
1012    // 8. parse '.'
1013    exponent = 0;
1014    if (radix == DECIMAL && *p == '.') {
1015        RETURN_IF_CONVERSION_END(++p, end, (digits > 0 || (digits == 0 && leadingZero)) ?
1016                                           (number * std::pow(radix, exponent)) : NAN_VALUE);
1017        while (ToDigit(*p) < radix) {
1018            --exponent;
1019            ++digits;
1020            if (++p == end) {
1021                break;
1022            }
1023        }
1024    }
1025    if (digits == 0 && !leadingZero) {
1026        // no digits there, like ".", "sss", or ".e1"
1027        return NAN_VALUE;
1028    }
1029    auto pEnd = p;
1030
1031    // 9. parse 'e/E' with '+/-'
1032    char exponentSign = '+';
1033    int additionalExponent = 0;
1034    constexpr int MAX_EXPONENT = INT32_MAX / 2;
1035    if (radix == DECIMAL && (p != end && (*p == 'e' || *p == 'E'))) {
1036        RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
1037
1038        // 10. parse exponent number
1039        if (*p == '+' || *p == '-') {
1040            exponentSign = static_cast<char>(*p);
1041            RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
1042        }
1043        uint8_t digit;
1044        while ((digit = ToDigit(*p)) < radix) {
1045            if (additionalExponent > static_cast<int>(MAX_EXPONENT / radix)) {
1046                additionalExponent = MAX_EXPONENT;
1047            } else {
1048                additionalExponent = additionalExponent * static_cast<int>(radix) + static_cast<int>(digit);
1049            }
1050            if (++p == end) {
1051                break;
1052            }
1053        }
1054    }
1055    exponent += (exponentSign == '-' ? -additionalExponent : additionalExponent);
1056    if (!ignoreTrailing && NumberHelper::GotoNonspace(&p, end)) {
1057        return NAN_VALUE;
1058    }
1059
1060    // 10. build StringNumericLiteral string
1061    CString buffer;
1062    if (sign == Sign::NEG) {
1063        buffer += "-";
1064    }
1065    for (uint8_t *i = pStart; i < pEnd; ++i) {  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1066        if (*i != static_cast<uint8_t>('.')) {
1067            buffer += *i;
1068        }
1069    }
1070
1071    // 11. convert none-prefix radix string
1072    return Strtod(buffer.c_str(), exponent, radix);
1073}
1074
1075double NumberHelper::Strtod(const char *str, int exponent, uint8_t radix)
1076{
1077    ASSERT(str != nullptr);
1078    ASSERT(radix >= base::MIN_RADIX && radix <= base::MAX_RADIX);
1079    auto p = const_cast<char *>(str);
1080    Sign sign = Sign::NONE;
1081    uint64_t number = 0;
1082    uint64_t numberMax = (UINT64_MAX - (radix - 1)) / radix;
1083    double result = 0.0;
1084    if (*p == '-') {
1085        sign = Sign::NEG;
1086        ++p;
1087    }
1088    while (*p == '0') {
1089        ++p;
1090    }
1091    while (*p != '\0') {
1092        uint8_t digit = ToDigit(static_cast<uint8_t>(*p));
1093        if (digit >= radix) {
1094            break;
1095        }
1096        if (number < numberMax) {
1097            number = number * radix + digit;
1098        } else {
1099            ++exponent;
1100        }
1101        ++p;
1102    }
1103
1104    // cal pow
1105    int exponentAbs = exponent < 0 ? -exponent : exponent;
1106    double powVal = ((radix == DECIMAL) && (exponentAbs < POWERS_OF_TEN_SIZE)) ?
1107        POWERS_OF_TEN[exponentAbs] : std::pow(radix, exponentAbs);
1108    if (exponent < 0) {
1109        result = number / powVal;
1110    } else {
1111        result = number * powVal;
1112    }
1113    return sign == Sign::NEG ? -result : result;
1114}
1115
1116int32_t NumberHelper::DoubleToInt(double d, size_t bits)
1117{
1118    int32_t ret = 0;
1119    auto u64 = bit_cast<uint64_t>(d);
1120    int exp = static_cast<int>((u64 & DOUBLE_EXPONENT_MASK) >> DOUBLE_SIGNIFICAND_SIZE) - DOUBLE_EXPONENT_BIAS;
1121    if (exp < static_cast<int>(bits - 1)) {
1122        // smaller than INT<bits>_MAX, fast conversion
1123        ret = static_cast<int32_t>(d);
1124    } else if (exp < static_cast<int>(bits + DOUBLE_SIGNIFICAND_SIZE)) {
1125        // Still has significand bits after mod 2^<bits>
1126        // Get low <bits> bits by shift left <64 - bits> and shift right <64 - bits>
1127        uint64_t value = (((u64 & DOUBLE_SIGNIFICAND_MASK) | DOUBLE_HIDDEN_BIT)
1128                          << (static_cast<uint32_t>(exp) - DOUBLE_SIGNIFICAND_SIZE + INT64_BITS - bits)) >>
1129                         (INT64_BITS - bits);
1130        ret = static_cast<int32_t>(value);
1131        if ((u64 & DOUBLE_SIGN_MASK) == DOUBLE_SIGN_MASK && ret != INT32_MIN) {
1132            ret = -ret;
1133        }
1134    } else {
1135        // No significand bits after mod 2^<bits>, contains NaN and INF
1136        ret = 0;
1137    }
1138    return ret;
1139}
1140
1141int32_t NumberHelper::DoubleInRangeInt32(double d)
1142{
1143    if (d > INT_MAX) {
1144        return INT_MAX;
1145    }
1146    if (d < INT_MIN) {
1147        return INT_MIN;
1148    }
1149    return base::NumberHelper::DoubleToInt(d, base::INT32_BITS);
1150}
1151
1152JSTaggedValue NumberHelper::StringToBigInt(JSThread *thread, JSHandle<JSTaggedValue> strVal)
1153{
1154    auto strObj = static_cast<EcmaString *>(strVal->GetTaggedObject());
1155    uint32_t strLen = EcmaStringAccessor(strObj).GetLength();
1156    if (strLen == 0) {
1157        return BigInt::Int32ToBigInt(thread, 0).GetTaggedValue();
1158    }
1159    CVector<uint8_t> buf;
1160    Span<const uint8_t> str = EcmaStringAccessor(strObj).ToUtf8Span(buf);
1161
1162    auto p = const_cast<uint8_t *>(str.begin());
1163    auto end = str.end();
1164    // 1. skip space and line terminal
1165    if (!NumberHelper::GotoNonspace(&p, end)) {
1166        return BigInt::Int32ToBigInt(thread, 0).GetTaggedValue();
1167    }
1168    // 2. get bigint sign
1169    Sign sign = Sign::NONE;
1170    if (*p == '+') {
1171        RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
1172        sign = Sign::POS;
1173    } else if (*p == '-') {
1174        RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
1175        sign = Sign::NEG;
1176    }
1177    // 3. bigint not allow Infinity, decimal points, or exponents.
1178    if (isalpha(*p)) {
1179        return JSTaggedValue(NAN_VALUE);
1180    }
1181    // 4. get bigint radix
1182    uint8_t radix = DECIMAL;
1183    if (*p == '0') {
1184        if (++p == end) {
1185            return BigInt::Int32ToBigInt(thread, 0).GetTaggedValue();
1186        }
1187        if (*p == 'x' || *p == 'X') {
1188            RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
1189            if (sign != Sign::NONE) {
1190                return JSTaggedValue(NAN_VALUE);
1191            }
1192            radix = HEXADECIMAL;
1193        } else if (*p == 'o' || *p == 'O') {
1194            RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
1195            if (sign != Sign::NONE) {
1196                return JSTaggedValue(NAN_VALUE);
1197            }
1198            radix = OCTAL;
1199        } else if (*p == 'b' || *p == 'B') {
1200            RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
1201            if (sign != Sign::NONE) {
1202                return JSTaggedValue(NAN_VALUE);
1203            }
1204            radix = BINARY;
1205        }
1206    }
1207
1208    // 5. skip leading '0'
1209    while (*p == '0') {
1210        if (++p == end) {
1211            return BigInt::Int32ToBigInt(thread, 0).GetTaggedValue();
1212        }
1213    }
1214    // 6. parse to bigint
1215    CString buffer;
1216    do {
1217        uint8_t c = ToDigit(*p);
1218        if (c < radix) {
1219            buffer += *p;
1220        } else if (NumberHelper::GotoNonspace(&p, end)) {
1221            // illegal character
1222            return JSTaggedValue(NAN_VALUE);
1223        }
1224        // tail of string is space
1225    } while (++p < end);
1226    if (buffer.size() == 0) {
1227        return BigInt::Uint32ToBigInt(thread, 0).GetTaggedValue();
1228    }
1229    if (sign == Sign::NEG) {
1230        return BigIntHelper::SetBigInt(thread, "-" + buffer, radix).GetTaggedValue();
1231    }
1232    return BigIntHelper::SetBigInt(thread, buffer, radix).GetTaggedValue();
1233}
1234
1235void NumberHelper::GetBase(double d, int digits, int *decimalPoint, char *buf, char *bufTmp, int size)
1236{
1237    int result = snprintf_s(bufTmp, size, size - 1, "%+.*e", digits - 1, d);
1238    if (result == -1) {
1239        LOG_FULL(FATAL) << "snprintf_s failed";
1240        UNREACHABLE();
1241    }
1242    // mantissa
1243    buf[0] = bufTmp[1];
1244    if (digits > 1) {
1245        if (memcpy_s(buf + 1, digits, bufTmp + 2, digits) != EOK) { // 2 means add the point char to buf
1246            LOG_FULL(FATAL) << "memcpy_s failed";
1247            UNREACHABLE();
1248        }
1249    }
1250    buf[digits + 1] = '\0';
1251    // exponent
1252    *decimalPoint = atoi(bufTmp + digits + 2 + (digits > 1)) + 1; // 2 means ignore the integer and point
1253}
1254
1255int NumberHelper::GetMinmumDigits(double d, int *decimalPoint, char *buf)
1256{
1257    int digits = 0;
1258    char bufTmp[JS_DTOA_BUF_SIZE] = {0};
1259
1260    // find the minimum amount of digits
1261    int MinDigits = 1;
1262    int MaxDigits = DOUBLE_MAX_PRECISION;
1263    while (MinDigits < MaxDigits) {
1264        digits = (MinDigits + MaxDigits) / 2; // 2 :  Divide by 2
1265        GetBase(d, digits, decimalPoint, buf, bufTmp, sizeof(bufTmp));
1266        if (strtod(bufTmp, NULL) == d) {
1267            // no need to keep the trailing zeros
1268            while (digits >= 2 && buf[digits] == '0') { // 2 means ignore the integer and point
1269                digits--;
1270            }
1271            MaxDigits = digits;
1272        } else {
1273            MinDigits = digits + 1;
1274        }
1275    }
1276    digits = MaxDigits;
1277    GetBase(d, digits, decimalPoint, buf, bufTmp, sizeof(bufTmp));
1278
1279    return digits;
1280}
1281
1282bool NumberHelper::StringToInt64(const std::string& str, int64_t& value)
1283{
1284    if (str.empty()) {
1285        return false;
1286    }
1287
1288    char *end;
1289    errno = 0;
1290    value = std::strtoll(str.c_str(), &end, 0); // Automatic check of the number system
1291
1292    // If no number is converted
1293    if (end == str.c_str()) {
1294        return false;
1295    }
1296    // If there is a range error (too large or to small)
1297    if (errno == ERANGE && (value == LLONG_MAX || value == LLONG_MIN)) {
1298        return false;
1299    }
1300    // If the character string contains non-digit chaaracters
1301    if (*end != '\0') {
1302        return false;
1303    }
1304
1305    return true;
1306}
1307
1308uint64_t RandomGenerator::XorShift64(uint64_t *pVal)
1309{
1310    uint64_t x = *pVal;
1311    x ^= x >> RIGHT12;
1312    x ^= x << LEFT25;
1313    x ^= x >> RIGHT27;
1314    *pVal = x;
1315    return x * GET_MULTIPLY;
1316}
1317
1318void RandomGenerator::InitRandom(JSThread *thread)
1319{
1320    struct timeval tv;
1321    gettimeofday(&tv, NULL);
1322    randomState_ = static_cast<uint64_t>((tv.tv_sec * SECONDS_TO_SUBTLE) + tv.tv_usec);
1323    // the state must be non zero
1324    if (randomState_ == 0) {
1325        randomState_ = 1;
1326    }
1327    thread->SetRandomStatePtr(&randomState_);
1328}
1329
1330double RandomGenerator::NextDouble()
1331{
1332    uint64_t val = XorShift64(&randomState_);
1333    return ToDouble(val);
1334}
1335
1336double RandomGenerator::ToDouble(uint64_t state)
1337{
1338    uint64_t random = (state >> base::RIGHT12) | EXPONENTBITS_RANGE_IN_ONE_AND_TWO;
1339    return base::bit_cast<double>(random) - 1;
1340}
1341
1342int32_t RandomGenerator::Next(int bits)
1343{
1344    uint64_t val = XorShift64(&randomState_);
1345    return static_cast<int32_t>(val >> (INT64_BITS - bits));
1346}
1347
1348int32_t RandomGenerator::GenerateIdentityHash()
1349{
1350    return RandomGenerator::Next(INT32_BITS) & INT32_MAX;
1351}
1352}  // namespace panda::ecmascript::base
1353