diff --git a/BUILD.gn b/BUILD.gn index 2cd8063c99..81520a48ea 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -552,6 +552,7 @@ ecma_source = [ "ecmascript/base/array_helper.cpp", "ecmascript/base/atomic_helper.cpp", "ecmascript/base/builtins_base.cpp", + "ecmascript/base/dtoa_helper.cpp", "ecmascript/base/error_helper.cpp", "ecmascript/base/fast_json_stringifier.cpp", "ecmascript/base/json_helper.cpp", diff --git a/ecmascript/base/dtoa_helper.cpp b/ecmascript/base/dtoa_helper.cpp new file mode 100644 index 0000000000..8750602dd1 --- /dev/null +++ b/ecmascript/base/dtoa_helper.cpp @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/base/dtoa_helper.h" + +#ifndef UINT64_C2 +#define UINT64_C2(high32, low32) ((static_cast(high32) << 32) | static_cast(low32)) +#endif +namespace panda::ecmascript::base::dtoa { +DtoaHelper::DiyFp DtoaHelper::GetCachedPowerByIndex(size_t index) +{ + // 10^-348, 10^-340, ..., 10^340 + static const uint64_t kCachedPowers_F[] = { + UINT64_C2(0xfa8fd5a0, 0x081c0288), UINT64_C2(0xbaaee17f, 0xa23ebf76), + UINT64_C2(0x8b16fb20, 0x3055ac76), UINT64_C2(0xcf42894a, 0x5dce35ea), + UINT64_C2(0x9a6bb0aa, 0x55653b2d), UINT64_C2(0xe61acf03, 0x3d1a45df), + UINT64_C2(0xab70fe17, 0xc79ac6ca), UINT64_C2(0xff77b1fc, 0xbebcdc4f), + UINT64_C2(0xbe5691ef, 0x416bd60c), UINT64_C2(0x8dd01fad, 0x907ffc3c), + UINT64_C2(0xd3515c28, 0x31559a83), UINT64_C2(0x9d71ac8f, 0xada6c9b5), + UINT64_C2(0xea9c2277, 0x23ee8bcb), UINT64_C2(0xaecc4991, 0x4078536d), + UINT64_C2(0x823c1279, 0x5db6ce57), UINT64_C2(0xc2109436, 0x4dfb5637), + UINT64_C2(0x9096ea6f, 0x3848984f), UINT64_C2(0xd77485cb, 0x25823ac7), + UINT64_C2(0xa086cfcd, 0x97bf97f4), UINT64_C2(0xef340a98, 0x172aace5), + UINT64_C2(0xb23867fb, 0x2a35b28e), UINT64_C2(0x84c8d4df, 0xd2c63f3b), + UINT64_C2(0xc5dd4427, 0x1ad3cdba), UINT64_C2(0x936b9fce, 0xbb25c996), + UINT64_C2(0xdbac6c24, 0x7d62a584), UINT64_C2(0xa3ab6658, 0x0d5fdaf6), + UINT64_C2(0xf3e2f893, 0xdec3f126), UINT64_C2(0xb5b5ada8, 0xaaff80b8), + UINT64_C2(0x87625f05, 0x6c7c4a8b), UINT64_C2(0xc9bcff60, 0x34c13053), + UINT64_C2(0x964e858c, 0x91ba2655), UINT64_C2(0xdff97724, 0x70297ebd), + UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), UINT64_C2(0xf8a95fcf, 0x88747d94), + UINT64_C2(0xb9447093, 0x8fa89bcf), UINT64_C2(0x8a08f0f8, 0xbf0f156b), + UINT64_C2(0xcdb02555, 0x653131b6), UINT64_C2(0x993fe2c6, 0xd07b7fac), + UINT64_C2(0xe45c10c4, 0x2a2b3b06), UINT64_C2(0xaa242499, 0x697392d3), + UINT64_C2(0xfd87b5f2, 0x8300ca0e), UINT64_C2(0xbce50864, 0x92111aeb), + UINT64_C2(0x8cbccc09, 0x6f5088cc), UINT64_C2(0xd1b71758, 0xe219652c), + UINT64_C2(0x9c400000, 0x00000000), UINT64_C2(0xe8d4a510, 0x00000000), + UINT64_C2(0xad78ebc5, 0xac620000), UINT64_C2(0x813f3978, 0xf8940984), + UINT64_C2(0xc097ce7b, 0xc90715b3), UINT64_C2(0x8f7e32ce, 0x7bea5c70), + UINT64_C2(0xd5d238a4, 0xabe98068), UINT64_C2(0x9f4f2726, 0x179a2245), + UINT64_C2(0xed63a231, 0xd4c4fb27), UINT64_C2(0xb0de6538, 0x8cc8ada8), + UINT64_C2(0x83c7088e, 0x1aab65db), UINT64_C2(0xc45d1df9, 0x42711d9a), + UINT64_C2(0x924d692c, 0xa61be758), UINT64_C2(0xda01ee64, 0x1a708dea), + UINT64_C2(0xa26da399, 0x9aef774a), UINT64_C2(0xf209787b, 0xb47d6b85), + UINT64_C2(0xb454e4a1, 0x79dd1877), UINT64_C2(0x865b8692, 0x5b9bc5c2), + UINT64_C2(0xc83553c5, 0xc8965d3d), UINT64_C2(0x952ab45c, 0xfa97a0b3), + UINT64_C2(0xde469fbd, 0x99a05fe3), UINT64_C2(0xa59bc234, 0xdb398c25), + UINT64_C2(0xf6c69a72, 0xa3989f5c), UINT64_C2(0xb7dcbf53, 0x54e9bece), + UINT64_C2(0x88fcf317, 0xf22241e2), UINT64_C2(0xcc20ce9b, 0xd35c78a5), + UINT64_C2(0x98165af3, 0x7b2153df), UINT64_C2(0xe2a0b5dc, 0x971f303a), + UINT64_C2(0xa8d9d153, 0x5ce3b396), UINT64_C2(0xfb9b7cd9, 0xa4a7443c), + UINT64_C2(0xbb764c4c, 0xa7a44410), UINT64_C2(0x8bab8eef, 0xb6409c1a), + UINT64_C2(0xd01fef10, 0xa657842c), UINT64_C2(0x9b10a4e5, 0xe9913129), + UINT64_C2(0xe7109bfb, 0xa19c0c9d), UINT64_C2(0xac2820d9, 0x623bf429), + UINT64_C2(0x80444b5e, 0x7aa7cf85), UINT64_C2(0xbf21e440, 0x03acdd2d), + UINT64_C2(0x8e679c2f, 0x5e44ff8f), UINT64_C2(0xd433179d, 0x9c8cb841), + UINT64_C2(0x9e19db92, 0xb4e31ba9), UINT64_C2(0xeb96bf6e, 0xbadf77d9), + UINT64_C2(0xaf87023b, 0x9bf0ee6b) + }; + static const int16_t kCachedPowers_E[] = { + -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, + -954, -927, -901, -874, -847, -821, -794, -768, -741, -715, + -688, -661, -635, -608, -582, -555, -529, -502, -475, -449, + -422, -396, -369, -343, -316, -289, -263, -236, -210, -183, + -157, -130, -103, -77, -50, -24, 3, 30, 56, 83, + 109, 136, 162, 189, 216, 242, 269, 295, 322, 348, + 375, 402, 428, 455, 481, 508, 534, 561, 588, 614, + 641, 667, 694, 720, 747, 774, 800, 827, 853, 880, + 907, 933, 960, 986, 1013, 1039, 1066 + }; + return DtoaHelper::DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]); +} + +void DtoaHelper::GrisuRound(char *buffer, int len, uint64_t delta, uint64_t rest, uint64_t tenKappa, uint64_t distance) +{ + while (rest < distance && delta - rest >= tenKappa && + (rest + tenKappa < distance || distance - rest > rest + tenKappa - distance)) { + buffer[len - 1]--; + rest += tenKappa; + } +} + +int DtoaHelper::CountDecimalDigit32(uint32_t n) +{ + if (n < TEN) { + return 1; // 1: means the decimal digit + } else if (n < TEN2POW) { + return 2; // 2: means the decimal digit + } else if (n < TEN3POW) { + return 3; // 3: means the decimal digit + } else if (n < TEN4POW) { + return 4; // 4: means the decimal digit + } else if (n < TEN5POW) { + return 5; // 5: means the decimal digit + } else if (n < TEN6POW) { + return 6; // 6: means the decimal digit + } else if (n < TEN7POW) { + return 7; // 7: means the decimal digit + } else if (n < TEN8POW) { + return 8; // 8: means the decimal digit + } else { + return 9; // 9: means the decimal digit + } +} + +void DtoaHelper::DigitGen(const DiyFp &W, const DiyFp &Mp, uint64_t delta, char *buffer, int *len, int *K) +{ + const DiyFp one(uint64_t(1) << -Mp.e, Mp.e); + const DiyFp distance = Mp - W; + uint32_t p1 = static_cast(Mp.f >> -one.e); + uint64_t p2 = Mp.f & (one.f - 1); + int kappa = CountDecimalDigit32(p1); // kappa in [0, 9] + *len = 0; + while (kappa > 0) { + uint32_t d = 0; + switch (kappa) { + case 9: // 9: means the decimal digit + d = p1 / TEN8POW; + p1 %= TEN8POW; + break; + case 8: // 8: means the decimal digit + d = p1 / TEN7POW; + p1 %= TEN7POW; + break; + case 7: // 7: means the decimal digit + d = p1 / TEN6POW; + p1 %= TEN6POW; + break; + case 6: // 6: means the decimal digit + d = p1 / TEN5POW; + p1 %= TEN5POW; + break; + case 5: // 5: means the decimal digit + d = p1 / TEN4POW; + p1 %= TEN4POW; + break; + case 4: // 4: means the decimal digit + d = p1 / TEN3POW; + p1 %= TEN3POW; + break; + case 3: // 3: means the decimal digit + d = p1 / TEN2POW; + p1 %= TEN2POW; + break; + case 2: // 2: means the decimal digit + d = p1 / TEN; + p1 %= TEN; + break; + case 1: // 1: means the decimal digit + d = p1; + p1 = 0; + break; + default:; + } + if (d || *len) { + buffer[(*len)++] = static_cast('0' + static_cast(d)); + } + kappa--; + uint64_t tmp = (static_cast(p1) << -one.e) + p2; + if (tmp <= delta) { + *K += kappa; + GrisuRound(buffer, *len, delta, tmp, POW10[kappa] << -one.e, distance.f); + return; + } + } + + // kappa = 0 + for (;;) { + p2 *= TEN; + delta *= TEN; + char d = static_cast(p2 >> -one.e); + if (d || *len) { + buffer[(*len)++] = static_cast('0' + d); + } + p2 &= one.f - 1; + kappa--; + if (p2 < delta) { + *K += kappa; + int index = -kappa; + GrisuRound(buffer, *len, delta, p2, one.f, distance.f * (index < kIndex ? POW10[index] : 0)); + return; + } + } +} + +// Grisu2 algorithm use the extra capacity of the used integer type to shorten the produced output +void DtoaHelper::Grisu(double value, char *buffer, int *length, int *K) +{ + const DiyFp v(value); + DiyFp mMinus; + DiyFp mPlus; + v.NormalizedBoundaries(&mMinus, &mPlus); + + const DiyFp cached = GetCachedPower(mPlus.e, K); + const DiyFp W = v.Normalize() * cached; + DiyFp wPlus = mPlus * cached; + DiyFp wMinus = mMinus * cached; + wMinus.f++; + wPlus.f--; + DigitGen(W, wPlus, wPlus.f - wMinus.f, buffer, length, K); +} + +void DtoaHelper::Dtoa(double value, char *buffer, int *point, int *length) +{ + // Exceptional case such as NAN, 0.0, negative... are processed in DoubleToEcmaString + // So use Dtoa should avoid Exceptional case. + ASSERT(value > 0); + int k; + Grisu(value, buffer, length, &k); + *point = *length + k; +} +} \ No newline at end of file diff --git a/ecmascript/base/dtoa_helper.h b/ecmascript/base/dtoa_helper.h new file mode 100644 index 0000000000..76c509f51f --- /dev/null +++ b/ecmascript/base/dtoa_helper.h @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ECMASCRIPT_BASE_DTOA_HELPER_H +#define ECMASCRIPT_BASE_DTOA_HELPER_H + +#include +#include +#include +#include + +#include "ecmascript/common.h" +namespace panda::ecmascript::base::dtoa { +class DtoaHelper { +public: + static constexpr int CACHED_POWERS_OFFSET = 348; + static constexpr double kD_1_LOG2_10 = 0.30102999566398114; //1 / lg(10) + static constexpr int kQ = -61; + static constexpr int kIndex = 20; + static constexpr int MIN_DECIMAL_EXPONENT = -348; + static constexpr uint64_t POW10[] = { 1ULL, 10ULL, 100ULL, 1000ULL, 10000ULL, 100000ULL, 1000000ULL, + 10000000ULL, 100000000ULL, 1000000000ULL, 10000000000ULL, 100000000000ULL, + 1000000000000ULL, 10000000000000ULL, 100000000000000ULL, 1000000000000000ULL, + 10000000000000000ULL, 100000000000000000ULL, 1000000000000000000ULL, + 10000000000000000000ULL }; + + static constexpr uint32_t TEN = 10; + static constexpr uint32_t TEN2POW = 100; + static constexpr uint32_t TEN3POW = 1000; + static constexpr uint32_t TEN4POW = 10000; + static constexpr uint32_t TEN5POW = 100000; + static constexpr uint32_t TEN6POW = 1000000; + static constexpr uint32_t TEN7POW = 10000000; + static constexpr uint32_t TEN8POW = 100000000; + + // DiyFp is a floating-point number type, consists of a uint64 significand and one integer exponent. + struct DiyFp { + DiyFp() : f(), e() {} + DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {} + + explicit DiyFp(double d) + { + union { + double d; + uint64_t u64; + } u = { d }; + + int biased_e = static_cast((u.u64 & kDpExponentMask) >> kDpSignificandSize); + uint64_t significand = (u.u64 & kDpSignificandMask); + if (biased_e != 0) { + f = significand + kDpHiddenBit; + e = biased_e - kDpExponentBias; + } else { + f = significand; + e = kDpMinExponent + 1; + } + } + + DiyFp operator - (const DiyFp &rhs) const + { + return DiyFp(f - rhs.f, e); + } + + DiyFp operator*(const DiyFp &rhs) const + { + const uint64_t M32 = 0xFFFFFFFF; + const uint64_t a = f >> 32; + const uint64_t b = f & M32; + const uint64_t c = rhs.f >> 32; + const uint64_t d = rhs.f & M32; + const uint64_t ac = a * c; + const uint64_t bc = b * c; + const uint64_t ad = a * d; + const uint64_t bd = b * d; + uint64_t tmp = (bd >> kInt32Bits) + (ad & M32) + (bc & M32); + tmp += 1U << kRoundBits; // mult_round + return DiyFp(ac + (ad >> kInt32Bits) + (bc >> kInt32Bits) + (tmp >> kInt32Bits), e + rhs.e + kInt64Bits); + } + + DiyFp Normalize() const + { + DiyFp res = *this; + while (!(res.f & kDpHiddenBit)) { + res.f <<= 1; + res.e--; + } + res.f <<= (kDiySignificandSize - kDpSignificandSize - 1); + res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 1); + return res; + } + + DiyFp NormalizeBoundary() const + { + DiyFp res = *this; + while (!(res.f & (kDpHiddenBit << 1))) { + res.f <<= 1; + res.e--; + } + res.f <<= (kDiySignificandSize - kDpSignificandSize - 2); // 2: parameter + res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2); // 2: parameter + return res; + } + + void NormalizedBoundaries(DiyFp *minus, DiyFp *plus) const + { + DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary(); + DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1); // 2: parameter + mi.f <<= mi.e - pl.e; + mi.e = pl.e; + *plus = pl; + *minus = mi; + } + + static const int kInt64Bits = 64; + static const int kInt32Bits = 32; + static const int kRoundBits = 31; + static const int kDiySignificandSize = 64; + static const int kDpSignificandSize = 52; + static const int kDpExponentBias = 0x3FF + kDpSignificandSize; + static const int kDpMaxExponent = 0x7FF - kDpExponentBias; + static const int kDpMinExponent = -kDpExponentBias; + static const int kDpDenormalExponent = -kDpExponentBias + 1; + static const uint64_t kDpExponentMask = + (static_cast(0x7FF00000) << 32) | static_cast(0x00000000); + static const uint64_t kDpSignificandMask = + (static_cast(0x000FFFFF) << 32) | static_cast(0xFFFFFFFF); + static const uint64_t kDpHiddenBit = + (static_cast(0x00100000) << 32) | static_cast(0x00000000); + + uint64_t f; + int e; + }; + + static DiyFp GetCachedPower(int e, int *K) + { + // dk must be positive, so can do ceiling in positive + double dk = (kQ - e) * kD_1_LOG2_10 + CACHED_POWERS_OFFSET - 1; + int k = static_cast(dk); + if (dk - k > 0.0) { + k++; + } + unsigned index = static_cast((k >> 3) + 1); // 3: parameter + *K = -(MIN_DECIMAL_EXPONENT + static_cast(index << 3)); // 3: parameter + return GetCachedPowerByIndex(index); + } + + static DiyFp GetCachedPowerByIndex(size_t index); + static void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t tenKappa, uint64_t distance); + static int CountDecimalDigit32(uint32_t n); + static void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K); + static void Grisu(double value, char* buffer, int* length, int* K); + static void Dtoa(double value, char* buffer, int* point, int* length); +}; +} +#endif \ No newline at end of file diff --git a/ecmascript/base/number_helper.cpp b/ecmascript/base/number_helper.cpp index ca56c33222..eaa527c32e 100644 --- a/ecmascript/base/number_helper.cpp +++ b/ecmascript/base/number_helper.cpp @@ -24,6 +24,7 @@ #include #include "ecmascript/base/builtins_base.h" +#include "ecmascript/base/dtoa_helper.h" #include "ecmascript/base/string_helper.h" #include "ecmascript/builtins/builtins_number.h" #include "ecmascript/ecma_string_table.h" @@ -633,61 +634,28 @@ JSHandle NumberHelper::DoubleToEcmaString(const JSThread *thread, do } ASSERT(d > 0); - - // 5. Otherwise, let n, k, and s be integers such that k ≥ 1, 10k−1 ≤ s < 10k, the Number value for s × 10n−k is m, - // and k is as small as possible. If there are multiple possibilities for s, choose the value of s for which s × - // 10n−k is closest in value to m. If there are two such possible values of s, choose the one that is even. Note - // that k is the number of digits in the decimal representation of s and that s is not divisible by 10. - if (0.1 <= d && d < 1) { // 0.1: 10 ** -1 - // Fast path. In this case, n==0, just need to calculate k and s. - std::string resultFast = "0."; - int64_t sFast = 0; - int kFast = 1; - int64_t power = 1; - while (kFast <= DOUBLE_MAX_PRECISION) { - power *= 10; // 10: base 10 - int digitFast = static_cast(d * power) % 10; // 10: base 10 - ASSERT(0 <= digitFast && digitFast <= 9); // 9: single digit max - sFast = sFast * 10 + digitFast; // 10: base 10 - resultFast += (digitFast + '0'); - if (sFast / static_cast(power) == d) { // s * (10 ** -k) - result += resultFast; - return factory->NewFromASCII(result.c_str()); - } - kFast++; - } - } char buffer[JS_DTOA_BUF_SIZE] = {0}; - int n = 0; - int k = GetMinmumDigits(d, &n, buffer); + int n; // decimal point + int k; // length + dtoa::DtoaHelper::Dtoa(d, buffer, &n, &k); //Fast Double To Ascii. std::string base = buffer; if (n > 0 && n <= MAX_DIGITS) { - base.erase(1, 1); if (k <= n) { - // 6. If k ≤ n ≤ 21, return the String consisting of the code units of the k digits of the decimal - // representation of s (in order, with no leading zeroes), followed by n−k occurrences of the code unit - // 0x0030 (DIGIT ZERO). + // 6. If k ≤ n ≤ 21 base += std::string(n - k, '0'); } else { - // 7. If 0 < n ≤ 21, return the String consisting of the code units of the most significant n digits of the - // decimal representation of s, followed by the code unit 0x002E (FULL STOP), followed by the code units of - // the remaining k−n digits of the decimal representation of s. + // 7. If 0 < n ≤ 21 base.insert(n, 1, '.'); } } else if (MIN_DIGITS < n && n <= 0) { - // 8. If −6 < n ≤ 0, return the String consisting of the code unit 0x0030 (DIGIT ZERO), followed by the code - // unit 0x002E (FULL STOP), followed by −n occurrences of the code unit 0x0030 (DIGIT ZERO), followed by the - // code units of the k digits of the decimal representation of s. - base.erase(1, 1); + // 8. If −6 < n ≤ 0 base = std::string("0.") + std::string(-n, '0') + base; } else { - if (k == 1) { - // 9. Otherwise, if k = 1, return the String consisting of the code unit of the single digit of s - base.erase(1, 1); + // 9. & 10. Otherwise + base.erase(1, k - 1); + if (k != 1) { + base += std::string(".") + std::string(buffer + 1); } - // followed by code unit 0x0065 (LATIN SMALL LETTER E), followed by the code unit 0x002B (PLUS SIGN) or the - // code unit 0x002D (HYPHEN-MINUS) according to whether n−1 is positive or negative, followed by the code - // units of the decimal representation of the integer abs(n−1) (with no leading zeroes). base += "e" + (n >= 1 ? std::string("+") : "") + std::to_string(n - 1); } result += base; diff --git a/ecmascript/base/tests/BUILD.gn b/ecmascript/base/tests/BUILD.gn index 0d366458b1..e1ff703876 100644 --- a/ecmascript/base/tests/BUILD.gn +++ b/ecmascript/base/tests/BUILD.gn @@ -79,6 +79,7 @@ host_unittest_action("Base_003_Test") { sources = [ # test file + "dtoa_helper_test.cpp", "number_helper_test.cpp", "string_helper_test.cpp", "typed_array_helper_test.cpp", diff --git a/ecmascript/base/tests/dtoa_helper_test.cpp b/ecmascript/base/tests/dtoa_helper_test.cpp new file mode 100644 index 0000000000..cec17ad8ee --- /dev/null +++ b/ecmascript/base/tests/dtoa_helper_test.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/base/dtoa_helper.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::base; +using namespace panda::ecmascript::base::dtoa; + +#define TEST_DTOA(d, str, buffer, n, k, e1, e2) \ + DtoaHelper::Dtoa(d, buffer, &(n), &(k)); \ + EXPECT_STREQ(str, buffer); \ + EXPECT_EQ(n, e1); \ + EXPECT_EQ(k, e2) + +namespace panda::test { +class DtoaHelperTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + EcmaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +protected: + template + inline To MemoryCast(const From &src) noexcept + { + static_assert(sizeof(To) == sizeof(From), "size of the types must be equal"); + To dst; + if (memcpy_s(&dst, sizeof(To), &src, sizeof(From)) != EOK) { + LOG_FULL(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + return dst; + } +}; + +HWTEST_F_L0(DtoaHelperTest, DoubleToAscii) +{ + char buffer1[128]; + int n1; //decimal_point + int k1; //length + TEST_DTOA(1.2345, "12345", buffer1, n1, k1, 1, 5); + + char buffer2[128]; + int n2; + int k2; + TEST_DTOA(1.2345678, "12345678", buffer2, n2, k2, 1, 8); + + char buffer3[128]; + int n3; + int k3; + TEST_DTOA(1e30, "1", buffer3, n3, k3, 31, 1); + + char buffer4[128]; + int n4; + int k4; + TEST_DTOA(79.39773355813419, "7939773355813419", buffer4, n4, k4, 2, 16); + + char buffer5[128]; + int n5; + int k5; + TEST_DTOA(0.0000001, "1", buffer5, n5, k5, -6, 1); + + char buffer6[128]; + int n6; + int k6; + TEST_DTOA(1.234567890123456e30, "1234567890123456", buffer6, n6, k6, 31, 16); + + char buffer7[128]; + int n7; + int k7; + TEST_DTOA(2.225073858507201e-308, "2225073858507201", buffer7, n7, k7, -307, 16); // Max subnormal positive double + + char buffer8[128]; + int n8; + int k8; + TEST_DTOA(2.2250738585072014e-308, "22250738585072014", buffer8, n8, k8, -307, 17); // Min normal positive double + + char buffer9[128]; + int n9; + int k9; + TEST_DTOA(1.7976931348623157e308, "17976931348623157", buffer9, n9, k9, 309, 17); // Max double + + char buffera[128]; + int na; + int ka; + TEST_DTOA(5e-301, "5", buffera, na, ka, -300, 1); +} +} // namespace panda::test diff --git a/ecmascript/base/tests/number_helper_test.cpp b/ecmascript/base/tests/number_helper_test.cpp index e32d1dd266..9b1ab036c8 100644 --- a/ecmascript/base/tests/number_helper_test.cpp +++ b/ecmascript/base/tests/number_helper_test.cpp @@ -157,6 +157,53 @@ HWTEST_F_L0(NumberHelperTest, DoubleToString_002) EXPECT_EQ(EcmaStringAccessor::Compare(instance, handleEcmaStr6, resultStr), 0); } +/** + * @tc.name: DoubleToEcmaString + * @tc.desc: This function Convert the double type data into a EcmaString. + * @tc.type: FUNC + * @tc.require: + */ +HWTEST_F_L0(NumberHelperTest, DoubleToEcmaString) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle resultStr1 = + factory->NewFromASCII("5562684646268003"); + double d1 = 5562684646268003; + JSHandle resultJSHandle1 = NumberHelper::DoubleToEcmaString(thread, d1); + EXPECT_EQ(EcmaStringAccessor::Compare(instance, resultJSHandle1, resultStr1), 0); + + JSHandle resultStr2 = + factory->NewFromASCII("0.005431"); + double d2 = 0.005431; + JSHandle resultJSHandle2 = NumberHelper::DoubleToEcmaString(thread, d2); + EXPECT_EQ(EcmaStringAccessor::Compare(instance, resultJSHandle2, resultStr2), 0); + + JSHandle resultStr3 = + factory->NewFromASCII("1.9045e-7"); + double d3 = 0.00000019045; + JSHandle resultJSHandle3 = NumberHelper::DoubleToEcmaString(thread, d3); + EXPECT_EQ(EcmaStringAccessor::Compare(instance, resultJSHandle3, resultStr3), 0); + + JSHandle resultStr4 = + factory->NewFromASCII("-79.39773355813419"); + double d4 = -79.39773355813419; + JSHandle resultJSHandle4 = NumberHelper::DoubleToEcmaString(thread, d4); + EXPECT_EQ(EcmaStringAccessor::Compare(instance, resultJSHandle4, resultStr4), 0); + + JSHandle resultStr5 = + factory->NewFromASCII("1e+21"); + double d5 = 1e21; + JSHandle resultJSHandle5 = NumberHelper::DoubleToEcmaString(thread, d5); + EXPECT_EQ(EcmaStringAccessor::Compare(instance, resultJSHandle5, resultStr5), 0); + + JSHandle resultStr6 = + factory->NewFromASCII("340000000000000000"); + double d6 = 340000000000000000; + JSHandle resultJSHandle6 = NumberHelper::DoubleToEcmaString(thread, d6); + EXPECT_EQ(EcmaStringAccessor::Compare(instance, resultJSHandle6, resultStr6), 0); +} + /** * @tc.name: IsEmptyString * @tc.desc: Check whether the character is empty string through "IsEmptyString" function. diff --git a/test/aottest/builtin_inlining/math/Cbrt/builtinMathCbrt.ts b/test/aottest/builtin_inlining/math/Cbrt/builtinMathCbrt.ts index 0f91a1e775..b4d67a37f9 100644 --- a/test/aottest/builtin_inlining/math/Cbrt/builtinMathCbrt.ts +++ b/test/aottest/builtin_inlining/math/Cbrt/builtinMathCbrt.ts @@ -39,8 +39,8 @@ let res:number = 1; print(Math.cbrt()); //: NaN // Check with single param -print(Math.cbrt(-0.027)); //: -0.29999999999999992 -print(Math.cbrt(0.125)); //: 0.49999999999999992 +print(Math.cbrt(-0.027)); //: -0.29999999999999993 +print(Math.cbrt(0.125)); //: 0.49999999999999994 print(Math.cbrt(1)); //: 1 print(Math.cbrt(8)); //: 2 print(Math.cbrt(2146689000)); //: 1290.0000000000002