1 // Copyright 2017 PDFium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7 #include "xfa/fgas/crt/cfgas_decimal.h"
8
9 #include <algorithm>
10 #include <limits>
11 #include <utility>
12
13 #include "core/fxcrt/fx_extension.h"
14
15 #define FXMATH_DECIMAL_SCALELIMIT 0x1c
16 #define FXMATH_DECIMAL_RSHIFT32BIT(x) ((x) >> 0x10 >> 0x10)
17 #define FXMATH_DECIMAL_LSHIFT32BIT(x) ((x) << 0x10 << 0x10)
18
19 namespace {
20
decimal_helper_div10(uint64_t & phi,uint64_t & pmid,uint64_t & plo)21 inline uint8_t decimal_helper_div10(uint64_t& phi,
22 uint64_t& pmid,
23 uint64_t& plo) {
24 uint8_t retVal;
25 pmid += FXMATH_DECIMAL_LSHIFT32BIT(phi % 0xA);
26 phi /= 0xA;
27 plo += FXMATH_DECIMAL_LSHIFT32BIT(pmid % 0xA);
28 pmid /= 0xA;
29 retVal = plo % 0xA;
30 plo /= 0xA;
31 return retVal;
32 }
33
decimal_helper_div10_any(uint64_t nums[],uint8_t numcount)34 inline uint8_t decimal_helper_div10_any(uint64_t nums[], uint8_t numcount) {
35 uint8_t retVal = 0;
36 for (int i = numcount - 1; i > 0; i--) {
37 nums[i - 1] += FXMATH_DECIMAL_LSHIFT32BIT(nums[i] % 0xA);
38 nums[i] /= 0xA;
39 }
40 if (numcount) {
41 retVal = nums[0] % 0xA;
42 nums[0] /= 0xA;
43 }
44 return retVal;
45 }
46
decimal_helper_mul10(uint64_t & phi,uint64_t & pmid,uint64_t & plo)47 inline void decimal_helper_mul10(uint64_t& phi, uint64_t& pmid, uint64_t& plo) {
48 plo *= 0xA;
49 pmid = pmid * 0xA + FXMATH_DECIMAL_RSHIFT32BIT(plo);
50 plo = (uint32_t)plo;
51 phi = phi * 0xA + FXMATH_DECIMAL_RSHIFT32BIT(pmid);
52 pmid = (uint32_t)pmid;
53 }
54
decimal_helper_mul10_any(uint64_t nums[],uint8_t numcount)55 inline void decimal_helper_mul10_any(uint64_t nums[], uint8_t numcount) {
56 nums[0] *= 0xA;
57 for (int i = 1; i < numcount; i++) {
58 nums[i] = nums[i] * 0xA + FXMATH_DECIMAL_RSHIFT32BIT(nums[i - 1]);
59 nums[i - 1] = (uint32_t)nums[i - 1];
60 }
61 }
62
decimal_helper_normalize(uint64_t & phi,uint64_t & pmid,uint64_t & plo)63 inline void decimal_helper_normalize(uint64_t& phi,
64 uint64_t& pmid,
65 uint64_t& plo) {
66 phi += FXMATH_DECIMAL_RSHIFT32BIT(pmid);
67 pmid = (uint32_t)pmid;
68 pmid += FXMATH_DECIMAL_RSHIFT32BIT(plo);
69 plo = (uint32_t)plo;
70 phi += FXMATH_DECIMAL_RSHIFT32BIT(pmid);
71 pmid = (uint32_t)pmid;
72 }
73
decimal_helper_normalize_any(uint64_t nums[],uint8_t len)74 inline void decimal_helper_normalize_any(uint64_t nums[], uint8_t len) {
75 for (int i = len - 2; i > 0; i--) {
76 nums[i + 1] += FXMATH_DECIMAL_RSHIFT32BIT(nums[i]);
77 nums[i] = (uint32_t)nums[i];
78 }
79 for (int i = 0; i < len - 1; i++) {
80 nums[i + 1] += FXMATH_DECIMAL_RSHIFT32BIT(nums[i]);
81 nums[i] = (uint32_t)nums[i];
82 }
83 }
84
decimal_helper_raw_compare_any(uint64_t a[],uint8_t al,uint64_t b[],uint8_t bl)85 inline int8_t decimal_helper_raw_compare_any(uint64_t a[],
86 uint8_t al,
87 uint64_t b[],
88 uint8_t bl) {
89 int8_t retVal = 0;
90 for (int i = std::max(al - 1, bl - 1); i >= 0; i--) {
91 uint64_t l = (i >= al ? 0 : a[i]), r = (i >= bl ? 0 : b[i]);
92 retVal += (l > r ? 1 : (l < r ? -1 : 0));
93 if (retVal)
94 return retVal;
95 }
96 return retVal;
97 }
98
decimal_helper_dec_any(uint64_t a[],uint8_t al)99 inline void decimal_helper_dec_any(uint64_t a[], uint8_t al) {
100 for (int i = 0; i < al; i++) {
101 if (a[i]--)
102 return;
103 }
104 }
105
decimal_helper_inc_any(uint64_t a[],uint8_t al)106 inline void decimal_helper_inc_any(uint64_t a[], uint8_t al) {
107 for (int i = 0; i < al; i++) {
108 a[i]++;
109 if ((uint32_t)a[i] == a[i])
110 return;
111 a[i] = 0;
112 }
113 }
114
decimal_helper_raw_mul(uint64_t a[],uint8_t al,uint64_t b[],uint8_t bl,uint64_t c[],uint8_t cl)115 inline void decimal_helper_raw_mul(uint64_t a[],
116 uint8_t al,
117 uint64_t b[],
118 uint8_t bl,
119 uint64_t c[],
120 uint8_t cl) {
121 ASSERT(al + bl <= cl);
122 for (int i = 0; i < cl; i++)
123 c[i] = 0;
124
125 for (int i = 0; i < al; i++) {
126 for (int j = 0; j < bl; j++) {
127 uint64_t m = (uint64_t)a[i] * b[j];
128 c[i + j] += (uint32_t)m;
129 c[i + j + 1] += FXMATH_DECIMAL_RSHIFT32BIT(m);
130 }
131 }
132 for (int i = 0; i < cl - 1; i++) {
133 c[i + 1] += FXMATH_DECIMAL_RSHIFT32BIT(c[i]);
134 c[i] = (uint32_t)c[i];
135 }
136 for (int i = 0; i < cl; i++)
137 c[i] = (uint32_t)c[i];
138 }
139
decimal_helper_raw_div(uint64_t a[],uint8_t al,uint64_t b[],uint8_t bl,uint64_t c[],uint8_t cl)140 inline void decimal_helper_raw_div(uint64_t a[],
141 uint8_t al,
142 uint64_t b[],
143 uint8_t bl,
144 uint64_t c[],
145 uint8_t cl) {
146 for (int i = 0; i < cl; i++)
147 c[i] = 0;
148
149 uint64_t left[16] = {0};
150 uint64_t right[16] = {0};
151 left[0] = 0;
152 for (int i = 0; i < al; i++)
153 right[i] = a[i];
154
155 uint64_t tmp[16];
156 while (decimal_helper_raw_compare_any(left, al, right, al) <= 0) {
157 uint64_t cur[16];
158 for (int i = 0; i < al; i++)
159 cur[i] = left[i] + right[i];
160
161 for (int i = al - 1; i >= 0; i--) {
162 if (i)
163 cur[i - 1] += FXMATH_DECIMAL_LSHIFT32BIT(cur[i] % 2);
164 cur[i] /= 2;
165 }
166
167 decimal_helper_raw_mul(cur, al, b, bl, tmp, 16);
168 switch (decimal_helper_raw_compare_any(tmp, 16, a, al)) {
169 case -1:
170 for (int i = 0; i < 16; i++)
171 left[i] = cur[i];
172
173 left[0]++;
174 decimal_helper_normalize_any(left, al);
175 break;
176 case 1:
177 for (int i = 0; i < 16; i++)
178 right[i] = cur[i];
179 decimal_helper_dec_any(right, al);
180 break;
181 case 0:
182 for (int i = 0; i < std::min(al, cl); i++)
183 c[i] = cur[i];
184 return;
185 }
186 }
187 for (int i = 0; i < std::min(al, cl); i++)
188 c[i] = left[i];
189 }
190
decimal_helper_outofrange(uint64_t a[],uint8_t al,uint8_t goal)191 inline bool decimal_helper_outofrange(uint64_t a[], uint8_t al, uint8_t goal) {
192 for (int i = goal; i < al; i++) {
193 if (a[i])
194 return true;
195 }
196 return false;
197 }
198
decimal_helper_shrinkintorange(uint64_t a[],uint8_t al,uint8_t goal,uint8_t & scale)199 inline void decimal_helper_shrinkintorange(uint64_t a[],
200 uint8_t al,
201 uint8_t goal,
202 uint8_t& scale) {
203 bool bRoundUp = false;
204 while (scale != 0 && (scale > FXMATH_DECIMAL_SCALELIMIT ||
205 decimal_helper_outofrange(a, al, goal))) {
206 bRoundUp = decimal_helper_div10_any(a, al) >= 5;
207 scale--;
208 }
209 if (bRoundUp) {
210 decimal_helper_normalize_any(a, goal);
211 decimal_helper_inc_any(a, goal);
212 }
213 }
214
decimal_helper_truncate(uint64_t & phi,uint64_t & pmid,uint64_t & plo,uint8_t & scale,uint8_t minscale=0)215 inline void decimal_helper_truncate(uint64_t& phi,
216 uint64_t& pmid,
217 uint64_t& plo,
218 uint8_t& scale,
219 uint8_t minscale = 0) {
220 while (scale > minscale) {
221 uint64_t thi = phi, tmid = pmid, tlo = plo;
222 if (decimal_helper_div10(thi, tmid, tlo) != 0)
223 break;
224
225 phi = thi;
226 pmid = tmid;
227 plo = tlo;
228 scale--;
229 }
230 }
231
232 } // namespace
233
234 CFGAS_Decimal::CFGAS_Decimal() = default;
235
CFGAS_Decimal(uint64_t val)236 CFGAS_Decimal::CFGAS_Decimal(uint64_t val)
237 : m_uMid(static_cast<uint32_t>(FXMATH_DECIMAL_RSHIFT32BIT(val))),
238 m_uLo(static_cast<uint32_t>(val)) {}
239
CFGAS_Decimal(uint32_t val)240 CFGAS_Decimal::CFGAS_Decimal(uint32_t val)
241 : m_uLo(static_cast<uint32_t>(val)) {}
242
CFGAS_Decimal(uint32_t lo,uint32_t mid,uint32_t hi,bool neg,uint8_t scale)243 CFGAS_Decimal::CFGAS_Decimal(uint32_t lo,
244 uint32_t mid,
245 uint32_t hi,
246 bool neg,
247 uint8_t scale)
248 : m_uHi(hi),
249 m_uMid(mid),
250 m_uLo(lo),
251 m_bNeg(neg && IsNotZero()),
252 m_uScale(scale > FXMATH_DECIMAL_SCALELIMIT ? 0 : scale) {}
253
CFGAS_Decimal(int32_t val)254 CFGAS_Decimal::CFGAS_Decimal(int32_t val) {
255 if (val >= 0) {
256 *this = CFGAS_Decimal(static_cast<uint32_t>(val));
257 } else if (val == std::numeric_limits<int32_t>::min()) {
258 *this = CFGAS_Decimal(static_cast<uint32_t>(val));
259 SetNegate();
260 } else {
261 *this = CFGAS_Decimal(static_cast<uint32_t>(-val));
262 SetNegate();
263 }
264 }
265
CFGAS_Decimal(float val,uint8_t scale)266 CFGAS_Decimal::CFGAS_Decimal(float val, uint8_t scale) {
267 float newval = fabs(val);
268 float divisor = powf(2.0, 64.0f);
269 uint64_t bottom64 = static_cast<uint64_t>(fmodf(newval, divisor));
270 uint64_t top64 = static_cast<uint64_t>(newval / divisor);
271 uint64_t plo = bottom64 & 0xFFFFFFFF;
272 uint64_t pmid = bottom64 >> 32;
273 uint64_t phi = top64 & 0xFFFFFFFF;
274
275 newval = fmodf(newval, 1.0f);
276 for (uint8_t iter = 0; iter < scale; iter++) {
277 decimal_helper_mul10(phi, pmid, plo);
278 newval *= 10;
279 plo += static_cast<uint64_t>(newval);
280 newval = fmodf(newval, 1.0f);
281 }
282
283 plo += FXSYS_roundf(newval);
284 decimal_helper_normalize(phi, pmid, plo);
285 m_uHi = static_cast<uint32_t>(phi);
286 m_uMid = static_cast<uint32_t>(pmid);
287 m_uLo = static_cast<uint32_t>(plo);
288 m_bNeg = val < 0 && IsNotZero();
289 m_uScale = scale;
290 }
291
CFGAS_Decimal(WideStringView strObj)292 CFGAS_Decimal::CFGAS_Decimal(WideStringView strObj) {
293 const wchar_t* str = strObj.unterminated_c_str();
294 const wchar_t* strBound = str + strObj.GetLength();
295 bool pointmet = false;
296 bool negmet = false;
297 uint8_t scale = 0;
298 m_uHi = 0;
299 m_uMid = 0;
300 m_uLo = 0;
301 while (str != strBound && *str == ' ')
302 str++;
303 if (str != strBound && *str == '-') {
304 negmet = 1;
305 str++;
306 } else if (str != strBound && *str == '+') {
307 str++;
308 }
309
310 while (str != strBound && (FXSYS_IsDecimalDigit(*str) || *str == '.') &&
311 scale < FXMATH_DECIMAL_SCALELIMIT) {
312 if (*str == '.') {
313 if (!pointmet)
314 pointmet = 1;
315 } else {
316 m_uHi = m_uHi * 0xA + FXMATH_DECIMAL_RSHIFT32BIT((uint64_t)m_uMid * 0xA);
317 m_uMid = m_uMid * 0xA + FXMATH_DECIMAL_RSHIFT32BIT((uint64_t)m_uLo * 0xA);
318 m_uLo = m_uLo * 0xA + (*str - '0');
319 if (pointmet)
320 scale++;
321 }
322 str++;
323 }
324 m_bNeg = negmet && IsNotZero();
325 m_uScale = scale;
326 }
327
ToWideString() const328 WideString CFGAS_Decimal::ToWideString() const {
329 WideString retString;
330 WideString tmpbuf;
331 uint64_t phi = m_uHi;
332 uint64_t pmid = m_uMid;
333 uint64_t plo = m_uLo;
334 while (phi || pmid || plo)
335 tmpbuf += decimal_helper_div10(phi, pmid, plo) + '0';
336
337 uint8_t outputlen = (uint8_t)tmpbuf.GetLength();
338 uint8_t scale = m_uScale;
339 while (scale >= outputlen) {
340 tmpbuf += '0';
341 outputlen++;
342 }
343 if (m_bNeg && IsNotZero())
344 retString += '-';
345
346 for (uint8_t idx = 0; idx < outputlen; idx++) {
347 if (idx == (outputlen - scale) && scale != 0)
348 retString += '.';
349 retString += tmpbuf[outputlen - 1 - idx];
350 }
351 return retString;
352 }
353
ToFloat() const354 float CFGAS_Decimal::ToFloat() const {
355 return static_cast<float>(ToDouble());
356 }
357
ToDouble() const358 double CFGAS_Decimal::ToDouble() const {
359 double pow = (double)(1 << 16) * (1 << 16);
360 double base = static_cast<double>(m_uHi) * pow * pow +
361 static_cast<double>(m_uMid) * pow + static_cast<double>(m_uLo);
362 return (m_bNeg ? -1 : 1) * base * powf(10.0f, -m_uScale);
363 }
364
SetScale(uint8_t newscale)365 void CFGAS_Decimal::SetScale(uint8_t newscale) {
366 uint8_t oldscale = m_uScale;
367 if (oldscale == newscale)
368 return;
369
370 uint64_t phi = m_uHi;
371 uint64_t pmid = m_uMid;
372 uint64_t plo = m_uLo;
373 if (newscale > oldscale) {
374 for (uint8_t iter = 0; iter < newscale - oldscale; iter++)
375 decimal_helper_mul10(phi, pmid, plo);
376
377 m_uHi = static_cast<uint32_t>(phi);
378 m_uMid = static_cast<uint32_t>(pmid);
379 m_uLo = static_cast<uint32_t>(plo);
380 m_bNeg = m_bNeg && IsNotZero();
381 m_uScale = newscale;
382 } else {
383 uint64_t point5_hi = 0;
384 uint64_t point5_mid = 0;
385 uint64_t point5_lo = 5;
386 for (uint8_t iter = 0; iter < oldscale - newscale - 1; iter++)
387 decimal_helper_mul10(point5_hi, point5_mid, point5_lo);
388
389 phi += point5_hi;
390 pmid += point5_mid;
391 plo += point5_lo;
392 decimal_helper_normalize(phi, pmid, plo);
393 for (uint8_t iter = 0; iter < oldscale - newscale; iter++)
394 decimal_helper_div10(phi, pmid, plo);
395 }
396 m_uHi = static_cast<uint32_t>(phi);
397 m_uMid = static_cast<uint32_t>(pmid);
398 m_uLo = static_cast<uint32_t>(plo);
399 m_bNeg = m_bNeg && IsNotZero();
400 m_uScale = newscale;
401 }
402
SetNegate()403 void CFGAS_Decimal::SetNegate() {
404 if (IsNotZero())
405 m_bNeg = !m_bNeg;
406 }
407
operator *(const CFGAS_Decimal & val) const408 CFGAS_Decimal CFGAS_Decimal::operator*(const CFGAS_Decimal& val) const {
409 uint64_t a[3] = {m_uLo, m_uMid, m_uHi},
410 b[3] = {val.m_uLo, val.m_uMid, val.m_uHi};
411 uint64_t c[6];
412 decimal_helper_raw_mul(a, 3, b, 3, c, 6);
413 bool neg = m_bNeg ^ val.m_bNeg;
414 uint8_t scale = m_uScale + val.m_uScale;
415 decimal_helper_shrinkintorange(c, 6, 3, scale);
416 return CFGAS_Decimal(static_cast<uint32_t>(c[0]), static_cast<uint32_t>(c[1]),
417 static_cast<uint32_t>(c[2]), neg, scale);
418 }
419
operator /(const CFGAS_Decimal & val) const420 CFGAS_Decimal CFGAS_Decimal::operator/(const CFGAS_Decimal& val) const {
421 if (!val.IsNotZero())
422 return CFGAS_Decimal();
423
424 bool neg = m_bNeg ^ val.m_bNeg;
425 uint64_t a[7] = {m_uLo, m_uMid, m_uHi},
426 b[3] = {val.m_uLo, val.m_uMid, val.m_uHi}, c[7] = {0};
427 uint8_t scale = 0;
428 if (m_uScale < val.m_uScale) {
429 for (int i = val.m_uScale - m_uScale; i > 0; i--)
430 decimal_helper_mul10_any(a, 7);
431 } else {
432 scale = m_uScale - val.m_uScale;
433 }
434
435 uint8_t minscale = scale;
436 if (!IsNotZero())
437 return CFGAS_Decimal(0, 0, 0, 0, minscale);
438
439 while (!a[6]) {
440 decimal_helper_mul10_any(a, 7);
441 scale++;
442 }
443
444 decimal_helper_div10_any(a, 7);
445 scale--;
446 decimal_helper_raw_div(a, 6, b, 3, c, 7);
447 decimal_helper_shrinkintorange(c, 6, 3, scale);
448 decimal_helper_truncate(c[2], c[1], c[0], scale, minscale);
449 return CFGAS_Decimal(static_cast<uint32_t>(c[0]), static_cast<uint32_t>(c[1]),
450 static_cast<uint32_t>(c[2]), neg, scale);
451 }
452