mirror of
https://gitee.com/openharmony/arkcompiler_ets_runtime
synced 2024-11-23 18:20:04 +00:00
opt DoubleToString
issue:issue:#I9BP84 Signed-off-by: dov1s <maojunwei1@huawei.com> Change-Id: I4701f0c0e2cca61b0ad143dbab1c630f8ee6c49f
This commit is contained in:
parent
5d9d9e4477
commit
1fbd86c756
1
BUILD.gn
1
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",
|
||||
|
223
ecmascript/base/dtoa_helper.cpp
Normal file
223
ecmascript/base/dtoa_helper.cpp
Normal file
@ -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<uint64_t>(high32) << 32) | static_cast<uint64_t>(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<uint32_t>(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<char>('0' + static_cast<char>(d));
|
||||
}
|
||||
kappa--;
|
||||
uint64_t tmp = (static_cast<uint64_t>(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<char>(p2 >> -one.e);
|
||||
if (d || *len) {
|
||||
buffer[(*len)++] = static_cast<char>('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;
|
||||
}
|
||||
}
|
167
ecmascript/base/dtoa_helper.h
Normal file
167
ecmascript/base/dtoa_helper.h
Normal file
@ -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 <math.h>
|
||||
#include <stdint.h>
|
||||
#include <stdint.h>
|
||||
#include <limits>
|
||||
|
||||
#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<int>((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<uint64_t>(0x7FF00000) << 32) | static_cast<uint64_t>(0x00000000);
|
||||
static const uint64_t kDpSignificandMask =
|
||||
(static_cast<uint64_t>(0x000FFFFF) << 32) | static_cast<uint64_t>(0xFFFFFFFF);
|
||||
static const uint64_t kDpHiddenBit =
|
||||
(static_cast<uint64_t>(0x00100000) << 32) | static_cast<uint64_t>(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<int>(dk);
|
||||
if (dk - k > 0.0) {
|
||||
k++;
|
||||
}
|
||||
unsigned index = static_cast<unsigned>((k >> 3) + 1); // 3: parameter
|
||||
*K = -(MIN_DECIMAL_EXPONENT + static_cast<int>(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
|
@ -24,6 +24,7 @@
|
||||
#include <sys/time.h>
|
||||
|
||||
#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<EcmaString> 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<int64_t>(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<double>(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;
|
||||
|
@ -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",
|
||||
|
121
ecmascript/base/tests/dtoa_helper_test.cpp
Normal file
121
ecmascript/base/tests/dtoa_helper_test.cpp
Normal file
@ -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 <class To, class From>
|
||||
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
|
@ -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<EcmaString> resultStr1 =
|
||||
factory->NewFromASCII("5562684646268003");
|
||||
double d1 = 5562684646268003;
|
||||
JSHandle<EcmaString> resultJSHandle1 = NumberHelper::DoubleToEcmaString(thread, d1);
|
||||
EXPECT_EQ(EcmaStringAccessor::Compare(instance, resultJSHandle1, resultStr1), 0);
|
||||
|
||||
JSHandle<EcmaString> resultStr2 =
|
||||
factory->NewFromASCII("0.005431");
|
||||
double d2 = 0.005431;
|
||||
JSHandle<EcmaString> resultJSHandle2 = NumberHelper::DoubleToEcmaString(thread, d2);
|
||||
EXPECT_EQ(EcmaStringAccessor::Compare(instance, resultJSHandle2, resultStr2), 0);
|
||||
|
||||
JSHandle<EcmaString> resultStr3 =
|
||||
factory->NewFromASCII("1.9045e-7");
|
||||
double d3 = 0.00000019045;
|
||||
JSHandle<EcmaString> resultJSHandle3 = NumberHelper::DoubleToEcmaString(thread, d3);
|
||||
EXPECT_EQ(EcmaStringAccessor::Compare(instance, resultJSHandle3, resultStr3), 0);
|
||||
|
||||
JSHandle<EcmaString> resultStr4 =
|
||||
factory->NewFromASCII("-79.39773355813419");
|
||||
double d4 = -79.39773355813419;
|
||||
JSHandle<EcmaString> resultJSHandle4 = NumberHelper::DoubleToEcmaString(thread, d4);
|
||||
EXPECT_EQ(EcmaStringAccessor::Compare(instance, resultJSHandle4, resultStr4), 0);
|
||||
|
||||
JSHandle<EcmaString> resultStr5 =
|
||||
factory->NewFromASCII("1e+21");
|
||||
double d5 = 1e21;
|
||||
JSHandle<EcmaString> resultJSHandle5 = NumberHelper::DoubleToEcmaString(thread, d5);
|
||||
EXPECT_EQ(EcmaStringAccessor::Compare(instance, resultJSHandle5, resultStr5), 0);
|
||||
|
||||
JSHandle<EcmaString> resultStr6 =
|
||||
factory->NewFromASCII("340000000000000000");
|
||||
double d6 = 340000000000000000;
|
||||
JSHandle<EcmaString> 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.
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user