• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024-2025 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 "dtoa_helper.h"
17 #include "libpandabase/macros.h"
18 #include "globals.h"
19 
20 // Almost copy of ets_runtime/ecmascript/base/dtoa_helper.cpp
21 namespace ark::es2panda::util {
22 #ifndef UINT64_C2
ConstructLong(uint32_t high32,uint32_t low32)23 static constexpr uint64_t ConstructLong(uint32_t high32, uint32_t low32)
24 {
25     return ((static_cast<uint64_t>(high32) << BITS_PER_UINT32) | static_cast<uint64_t>(low32));
26 }
27 #define UINT64_C2 ConstructLong
28 #endif
29 
GetCachedPower(int e)30 DtoaHelper::DiyFp DtoaHelper::GetCachedPower(int e)
31 {
32     // dk must be positive, so can do ceiling in positive
33     double dk = (Q - e) * D_1_LOG2_10 + CACHED_POWERS_OFFSET - 1;
34     int k = static_cast<int>(dk);
35     if (dk - k > 0.0) {
36         k++;
37     }
38     auto index = (static_cast<uint32_t>(k) >> 3U) + 1;             // 3: parameter
39     k_ = -(MIN_DECIMAL_EXPONENT + static_cast<int>(index << 3U));  // 3: parameter
40     return GetCachedPowerByIndex(index);
41 }
42 
GetCachedPowerByIndex(std::size_t index)43 DtoaHelper::DiyFp DtoaHelper::GetCachedPowerByIndex(std::size_t index)
44 {
45     // 10^-348, 10^-340, ..., 10^340
46     static constexpr auto CACHED_POWERS_F = std::array {
47         UINT64_C2(0xfa8fd5a0, 0x081c0288),  UINT64_C2(0xbaaee17f, 0xa23ebf76), UINT64_C2(0x8b16fb20, 0x3055ac76),
48         UINT64_C2(0xcf42894a, 0x5dce35ea),  UINT64_C2(0x9a6bb0aa, 0x55653b2d), UINT64_C2(0xe61acf03, 0x3d1a45df),
49         UINT64_C2(0xab70fe17, 0xc79ac6ca),  UINT64_C2(0xff77b1fc, 0xbebcdc4f), UINT64_C2(0xbe5691ef, 0x416bd60c),
50         UINT64_C2(0x8dd01fad, 0x907ffc3c),  UINT64_C2(0xd3515c28, 0x31559a83), UINT64_C2(0x9d71ac8f, 0xada6c9b5),
51         UINT64_C2(0xea9c2277, 0x23ee8bcb),  UINT64_C2(0xaecc4991, 0x4078536d), UINT64_C2(0x823c1279, 0x5db6ce57),
52         UINT64_C2(0xc2109436, 0x4dfb5637),  UINT64_C2(0x9096ea6f, 0x3848984f), UINT64_C2(0xd77485cb, 0x25823ac7),
53         UINT64_C2(0xa086cfcd, 0x97bf97f4),  UINT64_C2(0xef340a98, 0x172aace5), UINT64_C2(0xb23867fb, 0x2a35b28e),
54         UINT64_C2(0x84c8d4df, 0xd2c63f3b),  UINT64_C2(0xc5dd4427, 0x1ad3cdba), UINT64_C2(0x936b9fce, 0xbb25c996),
55         UINT64_C2(0xdbac6c24, 0x7d62a584),  UINT64_C2(0xa3ab6658, 0x0d5fdaf6), UINT64_C2(0xf3e2f893, 0xdec3f126),
56         UINT64_C2(0xb5b5ada8, 0xaaff80b8),  UINT64_C2(0x87625f05, 0x6c7c4a8b), UINT64_C2(0xc9bcff60, 0x34c13053),
57         UINT64_C2(0x964e858c, 0x91ba2655),  UINT64_C2(0xdff97724, 0x70297ebd), UINT64_C2(0xa6dfbd9f, 0xb8e5b88f),
58         UINT64_C2(0xf8a95fcf, 0x88747d94),  UINT64_C2(0xb9447093, 0x8fa89bcf), UINT64_C2(0x8a08f0f8, 0xbf0f156b),
59         UINT64_C2(0xcdb02555, 0x653131b6),  UINT64_C2(0x993fe2c6, 0xd07b7fac), UINT64_C2(0xe45c10c4, 0x2a2b3b06),
60         UINT64_C2(0xaa242499, 0x697392d3),  UINT64_C2(0xfd87b5f2, 0x8300ca0e), UINT64_C2(0xbce50864, 0x92111aeb),
61         UINT64_C2(0x8cbccc09, 0x6f5088cc),  UINT64_C2(0xd1b71758, 0xe219652c), UINT64_C2(0x9c400000, 0x00000000),
62         UINT64_C2(0xe8d4a510, 0x00000000),  UINT64_C2(0xad78ebc5, 0xac620000), UINT64_C2(0x813f3978, 0xf8940984),
63         UINT64_C2(0xc097ce7b, 0xc90715b3),  UINT64_C2(0x8f7e32ce, 0x7bea5c70), UINT64_C2(0xd5d238a4, 0xabe98068),
64         UINT64_C2(0x9f4f2726, 0x179a2245),  UINT64_C2(0xed63a231, 0xd4c4fb27), UINT64_C2(0xb0de6538, 0x8cc8ada8),
65         UINT64_C2(0x83c7088e, 0x1a'ab65db), UINT64_C2(0xc45d1df9, 0x42711d9a), UINT64_C2(0x924d692c, 0xa61be758),
66         UINT64_C2(0xda01ee64, 0x1a708dea),  UINT64_C2(0xa26da399, 0x9aef774a), UINT64_C2(0xf209787b, 0xb47d6b85),
67         UINT64_C2(0xb454e4a1, 0x79dd1877),  UINT64_C2(0x865b8692, 0x5b9bc5c2), UINT64_C2(0xc83553c5, 0xc8965d3d),
68         UINT64_C2(0x952ab45c, 0xfa97a0b3),  UINT64_C2(0xde469fbd, 0x99a05fe3), UINT64_C2(0xa59bc234, 0xdb398c25),
69         UINT64_C2(0xf6c69a72, 0xa3989f5c),  UINT64_C2(0xb7dcbf53, 0x54e9bece), UINT64_C2(0x88fcf317, 0xf22241e2),
70         UINT64_C2(0xcc20ce9b, 0xd35c78a5),  UINT64_C2(0x98165af3, 0x7b2153df), UINT64_C2(0xe2a0b5dc, 0x971f303a),
71         UINT64_C2(0xa8d9d153, 0x5ce3b396),  UINT64_C2(0xfb9b7cd9, 0xa4a7443c), UINT64_C2(0xbb764c4c, 0xa7a44410),
72         UINT64_C2(0x8bab8eef, 0xb6409c1a),  UINT64_C2(0xd01fef10, 0xa657842c), UINT64_C2(0x9b10a4e5, 0xe9913129),
73         UINT64_C2(0xe7109bfb, 0xa19c0c9d),  UINT64_C2(0xac2820d9, 0x623bf429), UINT64_C2(0x80444b5e, 0x7aa7cf85),
74         UINT64_C2(0xbf21e440, 0x03acdd2d),  UINT64_C2(0x8e679c2f, 0x5e44ff8f), UINT64_C2(0xd433179d, 0x9c8cb841),
75         UINT64_C2(0x9e19db92, 0xb4e31ba9),  UINT64_C2(0xeb96bf6e, 0xbadf77d9), UINT64_C2(0xaf87023b, 0x9bf0ee6b)};
76     static constexpr auto CACHED_POWERS_E =
77         std::array {-1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954, -927, -901, -874, -847,
78                     -821,  -794,  -768,  -741,  -715,  -688,  -661,  -635,  -608,  -582, -555, -529, -502, -475, -449,
79                     -422,  -396,  -369,  -343,  -316,  -289,  -263,  -236,  -210,  -183, -157, -130, -103, -77,  -50,
80                     -24,   3,     30,    56,    83,    109,   136,   162,   189,   216,  242,  269,  295,  322,  348,
81                     375,   402,   428,   455,   481,   508,   534,   561,   588,   614,  641,  667,  694,  720,  747,
82                     774,   800,   827,   853,   880,   907,   933,   960,   986,   1013, 1039, 1066};
83     return DtoaHelper::DiyFp(CACHED_POWERS_F[index], static_cast<int16_t>(CACHED_POWERS_E[index]));
84 }
85 
GrisuRound(uint64_t delta,uint64_t rest,uint64_t tenKappa,uint64_t distance)86 void DtoaHelper::GrisuRound(uint64_t delta, uint64_t rest, uint64_t tenKappa, uint64_t distance)
87 {
88     while (rest < distance && delta - rest >= tenKappa &&
89            (rest + tenKappa < distance || distance - rest > rest + tenKappa - distance)) {
90         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
91         buffer_[length_ - 1]--;
92         rest += tenKappa;
93     }
94 }
95 
96 constexpr auto MAX_DIGITS = 9;
CountDecimalDigit32(uint32_t n)97 int DtoaHelper::CountDecimalDigit32(uint32_t n)
98 {
99     ES2PANDA_ASSERT(n < POW10[MAX_DIGITS]);
100     for (int i = 1; i < MAX_DIGITS; i++) {
101         if (n < POW10[i]) {
102             return i;
103         }
104     }
105     return MAX_DIGITS;
106 }
107 
PopDigit(int kappa,uint32_t & p1)108 uint32_t DtoaHelper::PopDigit(int kappa, uint32_t &p1)
109 {
110     uint32_t d;
111     switch (kappa) {
112         case MAX_DIGITS:  // 9: number of decimal digits
113             d = p1 / TEN8POW;
114             p1 %= TEN8POW;
115             break;
116         case 8:  // 8: number of decimal digits
117             d = p1 / TEN7POW;
118             p1 %= TEN7POW;
119             break;
120         case 7:  // 7: number of decimal digits
121             d = p1 / TEN6POW;
122             p1 %= TEN6POW;
123             break;
124         case 6:  // 6: number of decimal digits
125             d = p1 / TEN5POW;
126             p1 %= TEN5POW;
127             break;
128         case 5:  // 5: number of decimal digits
129             d = p1 / TEN4POW;
130             p1 %= TEN4POW;
131             break;
132         case 4:  // 4: number of decimal digits
133             d = p1 / TEN3POW;
134             p1 %= TEN3POW;
135             break;
136         case 3:  // 3: number of decimal digits
137             d = p1 / TEN2POW;
138             p1 %= TEN2POW;
139             break;
140         case 2:  // 2: number of decimal digits
141             d = p1 / TEN;
142             p1 %= TEN;
143             break;
144         case 1:  // 1: number of decimal digits
145             d = p1;
146             p1 = 0;
147             break;
148         default:
149             ES2PANDA_UNREACHABLE();
150     }
151     return d;
152 }
153 
DigitGen(const DiyFp & w,const DiyFp & mp,uint64_t delta)154 void DtoaHelper::DigitGen(const DiyFp &w, const DiyFp &mp, uint64_t delta)
155 {
156     ES2PANDA_ASSERT(mp.e_ <= 0);
157     const DiyFp one(uint64_t(1) << static_cast<uint32_t>(-mp.e_), mp.e_);
158     const DiyFp distance = mp - w;
159     ES2PANDA_ASSERT(one.e_ <= 0);
160     auto p1 = static_cast<uint32_t>(mp.f_ >> static_cast<uint32_t>(-one.e_));
161     uint64_t p2 = mp.f_ & (one.f_ - 1);
162     int kappa = CountDecimalDigit32(p1);  // kappa in [0, 9]
163     length_ = 0;
164     while (kappa > 0) {
165         uint32_t d = PopDigit(kappa, p1);
166         if (d != 0 || length_ != 0) {
167             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
168             buffer_[length_++] = static_cast<char>('0' + static_cast<char>(d));
169         }
170         kappa--;
171         uint64_t tmp = (static_cast<uint64_t>(p1) << static_cast<uint32_t>(-one.e_)) + p2;
172         if (tmp <= delta) {
173             k_ += kappa;
174             GrisuRound(delta, tmp, POW10[kappa] << static_cast<uint32_t>(-one.e_), distance.f_);
175             return;
176         }
177     }
178 
179     // kappa = 0
180     do {
181         p2 *= TEN;
182         delta *= TEN;
183         char d = static_cast<char>(p2 >> static_cast<uint32_t>(-one.e_));
184         if (d != 0 || length_ != 0) {
185             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
186             buffer_[length_++] = static_cast<char>('0' + d);
187         }
188         p2 &= one.f_ - 1;
189         kappa--;
190     } while (p2 >= delta);
191     k_ += kappa;
192     int index = -kappa;
193     GrisuRound(delta, p2, one.f_, distance.f_ * (index < INDEX ? POW10[index] : 0));
194 }
195 
196 // Grisu2  algorithm use the extra capacity of the used integer type to shorten the produced output
Grisu(double value)197 void DtoaHelper::Grisu(double value)
198 {
199     const DiyFp v(value);
200     DiyFp mMinus;
201     DiyFp mPlus;
202     v.NormalizedBoundaries(&mMinus, &mPlus);
203 
204     const DiyFp cached = GetCachedPower(mPlus.e_);
205     const DiyFp w = v.Normalize() * cached;
206     DiyFp wPlus = mPlus * cached;
207     DiyFp wMinus = mMinus * cached;
208     wMinus.f_++;
209     wPlus.f_--;
210     DigitGen(w, wPlus, wPlus.f_ - wMinus.f_);
211 }
212 
Dtoa(double value)213 void DtoaHelper::Dtoa(double value)
214 {
215     // Exceptional case such as NAN, 0.0, negative... are processed in DoubleToEcmaString
216     // So use Dtoa should avoid Exceptional case.
217     ES2PANDA_ASSERT(value > 0);
218     Grisu(value);
219     point_ = length_ + k_;
220 }
221 }  // namespace ark::es2panda::util