xref: /aosp_15_r20/external/pdfium/xfa/fgas/crt/cfgas_decimal.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
1 // Copyright 2017 The PDFium Authors
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 <math.h>
10 
11 #include <algorithm>
12 #include <limits>
13 #include <utility>
14 
15 #include "core/fxcrt/fx_extension.h"
16 #include "third_party/base/check.h"
17 
18 #define FXMATH_DECIMAL_SCALELIMIT 0x1c
19 #define FXMATH_DECIMAL_RSHIFT32BIT(x) ((x) >> 0x10 >> 0x10)
20 #define FXMATH_DECIMAL_LSHIFT32BIT(x) ((x) << 0x10 << 0x10)
21 
22 namespace {
23 
decimal_helper_div10(uint64_t & phi,uint64_t & pmid,uint64_t & plo)24 inline uint8_t decimal_helper_div10(uint64_t& phi,
25                                     uint64_t& pmid,
26                                     uint64_t& plo) {
27   uint8_t retVal;
28   pmid += FXMATH_DECIMAL_LSHIFT32BIT(phi % 0xA);
29   phi /= 0xA;
30   plo += FXMATH_DECIMAL_LSHIFT32BIT(pmid % 0xA);
31   pmid /= 0xA;
32   retVal = plo % 0xA;
33   plo /= 0xA;
34   return retVal;
35 }
36 
decimal_helper_div10_any(uint64_t nums[],uint8_t numcount)37 inline uint8_t decimal_helper_div10_any(uint64_t nums[], uint8_t numcount) {
38   uint8_t retVal = 0;
39   for (int i = numcount - 1; i > 0; i--) {
40     nums[i - 1] += FXMATH_DECIMAL_LSHIFT32BIT(nums[i] % 0xA);
41     nums[i] /= 0xA;
42   }
43   if (numcount) {
44     retVal = nums[0] % 0xA;
45     nums[0] /= 0xA;
46   }
47   return retVal;
48 }
49 
decimal_helper_mul10(uint64_t & phi,uint64_t & pmid,uint64_t & plo)50 inline void decimal_helper_mul10(uint64_t& phi, uint64_t& pmid, uint64_t& plo) {
51   plo *= 0xA;
52   pmid = pmid * 0xA + FXMATH_DECIMAL_RSHIFT32BIT(plo);
53   plo = (uint32_t)plo;
54   phi = phi * 0xA + FXMATH_DECIMAL_RSHIFT32BIT(pmid);
55   pmid = (uint32_t)pmid;
56 }
57 
decimal_helper_mul10_any(uint64_t nums[],uint8_t numcount)58 inline void decimal_helper_mul10_any(uint64_t nums[], uint8_t numcount) {
59   nums[0] *= 0xA;
60   for (int i = 1; i < numcount; i++) {
61     nums[i] = nums[i] * 0xA + FXMATH_DECIMAL_RSHIFT32BIT(nums[i - 1]);
62     nums[i - 1] = (uint32_t)nums[i - 1];
63   }
64 }
65 
decimal_helper_normalize(uint64_t & phi,uint64_t & pmid,uint64_t & plo)66 inline void decimal_helper_normalize(uint64_t& phi,
67                                      uint64_t& pmid,
68                                      uint64_t& plo) {
69   phi += FXMATH_DECIMAL_RSHIFT32BIT(pmid);
70   pmid = (uint32_t)pmid;
71   pmid += FXMATH_DECIMAL_RSHIFT32BIT(plo);
72   plo = (uint32_t)plo;
73   phi += FXMATH_DECIMAL_RSHIFT32BIT(pmid);
74   pmid = (uint32_t)pmid;
75 }
76 
decimal_helper_normalize_any(uint64_t nums[],uint8_t len)77 inline void decimal_helper_normalize_any(uint64_t nums[], uint8_t len) {
78   for (int i = len - 2; i > 0; i--) {
79     nums[i + 1] += FXMATH_DECIMAL_RSHIFT32BIT(nums[i]);
80     nums[i] = (uint32_t)nums[i];
81   }
82   for (int i = 0; i < len - 1; i++) {
83     nums[i + 1] += FXMATH_DECIMAL_RSHIFT32BIT(nums[i]);
84     nums[i] = (uint32_t)nums[i];
85   }
86 }
87 
decimal_helper_raw_compare_any(uint64_t a[],uint8_t al,uint64_t b[],uint8_t bl)88 inline int8_t decimal_helper_raw_compare_any(uint64_t a[],
89                                              uint8_t al,
90                                              uint64_t b[],
91                                              uint8_t bl) {
92   int8_t retVal = 0;
93   for (int i = std::max(al - 1, bl - 1); i >= 0; i--) {
94     uint64_t l = (i >= al ? 0 : a[i]), r = (i >= bl ? 0 : b[i]);
95     retVal += (l > r ? 1 : (l < r ? -1 : 0));
96     if (retVal)
97       return retVal;
98   }
99   return retVal;
100 }
101 
decimal_helper_dec_any(uint64_t a[],uint8_t al)102 inline void decimal_helper_dec_any(uint64_t a[], uint8_t al) {
103   for (int i = 0; i < al; i++) {
104     if (a[i]--)
105       return;
106   }
107 }
108 
decimal_helper_inc_any(uint64_t a[],uint8_t al)109 inline void decimal_helper_inc_any(uint64_t a[], uint8_t al) {
110   for (int i = 0; i < al; i++) {
111     a[i]++;
112     if ((uint32_t)a[i] == a[i])
113       return;
114     a[i] = 0;
115   }
116 }
117 
decimal_helper_raw_mul(uint64_t a[],uint8_t al,uint64_t b[],uint8_t bl,uint64_t c[],uint8_t cl)118 inline void decimal_helper_raw_mul(uint64_t a[],
119                                    uint8_t al,
120                                    uint64_t b[],
121                                    uint8_t bl,
122                                    uint64_t c[],
123                                    uint8_t cl) {
124   DCHECK(al + bl <= cl);
125   for (int i = 0; i < cl; i++)
126     c[i] = 0;
127 
128   for (int i = 0; i < al; i++) {
129     for (int j = 0; j < bl; j++) {
130       uint64_t m = (uint64_t)a[i] * b[j];
131       c[i + j] += (uint32_t)m;
132       c[i + j + 1] += FXMATH_DECIMAL_RSHIFT32BIT(m);
133     }
134   }
135   for (int i = 0; i < cl - 1; i++) {
136     c[i + 1] += FXMATH_DECIMAL_RSHIFT32BIT(c[i]);
137     c[i] = (uint32_t)c[i];
138   }
139   for (int i = 0; i < cl; i++)
140     c[i] = (uint32_t)c[i];
141 }
142 
decimal_helper_raw_div(uint64_t a[],uint8_t al,uint64_t b[],uint8_t bl,uint64_t c[],uint8_t cl)143 inline void decimal_helper_raw_div(uint64_t a[],
144                                    uint8_t al,
145                                    uint64_t b[],
146                                    uint8_t bl,
147                                    uint64_t c[],
148                                    uint8_t cl) {
149   for (int i = 0; i < cl; i++)
150     c[i] = 0;
151 
152   uint64_t left[16] = {0};
153   uint64_t right[16] = {0};
154   left[0] = 0;
155   for (int i = 0; i < al; i++)
156     right[i] = a[i];
157 
158   uint64_t tmp[16];
159   while (decimal_helper_raw_compare_any(left, al, right, al) <= 0) {
160     uint64_t cur[16];
161     for (int i = 0; i < al; i++)
162       cur[i] = left[i] + right[i];
163 
164     for (int i = al - 1; i >= 0; i--) {
165       if (i)
166         cur[i - 1] += FXMATH_DECIMAL_LSHIFT32BIT(cur[i] % 2);
167       cur[i] /= 2;
168     }
169 
170     decimal_helper_raw_mul(cur, al, b, bl, tmp, 16);
171     switch (decimal_helper_raw_compare_any(tmp, 16, a, al)) {
172       case -1:
173         for (int i = 0; i < 16; i++)
174           left[i] = cur[i];
175 
176         left[0]++;
177         decimal_helper_normalize_any(left, al);
178         break;
179       case 1:
180         for (int i = 0; i < 16; i++)
181           right[i] = cur[i];
182         decimal_helper_dec_any(right, al);
183         break;
184       case 0:
185         for (int i = 0; i < std::min(al, cl); i++)
186           c[i] = cur[i];
187         return;
188     }
189   }
190   for (int i = 0; i < std::min(al, cl); i++)
191     c[i] = left[i];
192 }
193 
decimal_helper_outofrange(uint64_t a[],uint8_t al,uint8_t goal)194 inline bool decimal_helper_outofrange(uint64_t a[], uint8_t al, uint8_t goal) {
195   for (int i = goal; i < al; i++) {
196     if (a[i])
197       return true;
198   }
199   return false;
200 }
201 
decimal_helper_shrinkintorange(uint64_t a[],uint8_t al,uint8_t goal,uint8_t & scale)202 inline void decimal_helper_shrinkintorange(uint64_t a[],
203                                            uint8_t al,
204                                            uint8_t goal,
205                                            uint8_t& scale) {
206   bool bRoundUp = false;
207   while (scale != 0 && (scale > FXMATH_DECIMAL_SCALELIMIT ||
208                         decimal_helper_outofrange(a, al, goal))) {
209     bRoundUp = decimal_helper_div10_any(a, al) >= 5;
210     scale--;
211   }
212   if (bRoundUp) {
213     decimal_helper_normalize_any(a, goal);
214     decimal_helper_inc_any(a, goal);
215   }
216 }
217 
decimal_helper_truncate(uint64_t & phi,uint64_t & pmid,uint64_t & plo,uint8_t & scale,uint8_t minscale=0)218 inline void decimal_helper_truncate(uint64_t& phi,
219                                     uint64_t& pmid,
220                                     uint64_t& plo,
221                                     uint8_t& scale,
222                                     uint8_t minscale = 0) {
223   while (scale > minscale) {
224     uint64_t thi = phi, tmid = pmid, tlo = plo;
225     if (decimal_helper_div10(thi, tmid, tlo) != 0)
226       break;
227 
228     phi = thi;
229     pmid = tmid;
230     plo = tlo;
231     scale--;
232   }
233 }
234 
235 }  // namespace
236 
237 CFGAS_Decimal::CFGAS_Decimal() = default;
238 
CFGAS_Decimal(uint64_t val)239 CFGAS_Decimal::CFGAS_Decimal(uint64_t val)
240     : m_uMid(static_cast<uint32_t>(FXMATH_DECIMAL_RSHIFT32BIT(val))),
241       m_uLo(static_cast<uint32_t>(val)) {}
242 
CFGAS_Decimal(uint32_t val)243 CFGAS_Decimal::CFGAS_Decimal(uint32_t val)
244     : m_uLo(static_cast<uint32_t>(val)) {}
245 
CFGAS_Decimal(uint32_t lo,uint32_t mid,uint32_t hi,bool neg,uint8_t scale)246 CFGAS_Decimal::CFGAS_Decimal(uint32_t lo,
247                              uint32_t mid,
248                              uint32_t hi,
249                              bool neg,
250                              uint8_t scale)
251     : m_uHi(hi),
252       m_uMid(mid),
253       m_uLo(lo),
254       m_bNeg(neg && IsNotZero()),
255       m_uScale(scale > FXMATH_DECIMAL_SCALELIMIT ? 0 : scale) {}
256 
CFGAS_Decimal(int32_t val)257 CFGAS_Decimal::CFGAS_Decimal(int32_t val) {
258   if (val >= 0) {
259     *this = CFGAS_Decimal(static_cast<uint32_t>(val));
260   } else if (val == std::numeric_limits<int32_t>::min()) {
261     *this = CFGAS_Decimal(static_cast<uint32_t>(val));
262     SetNegate();
263   } else {
264     *this = CFGAS_Decimal(static_cast<uint32_t>(-val));
265     SetNegate();
266   }
267 }
268 
CFGAS_Decimal(float val,uint8_t scale)269 CFGAS_Decimal::CFGAS_Decimal(float val, uint8_t scale) {
270   float newval = fabs(val);
271   float divisor = powf(2.0, 64.0f);
272   uint64_t bottom64 = static_cast<uint64_t>(fmodf(newval, divisor));
273   uint64_t top64 = static_cast<uint64_t>(newval / divisor);
274   uint64_t plo = bottom64 & 0xFFFFFFFF;
275   uint64_t pmid = bottom64 >> 32;
276   uint64_t phi = top64 & 0xFFFFFFFF;
277 
278   newval = fmodf(newval, 1.0f);
279   for (uint8_t iter = 0; iter < scale; iter++) {
280     decimal_helper_mul10(phi, pmid, plo);
281     newval *= 10;
282     plo += static_cast<uint64_t>(newval);
283     newval = fmodf(newval, 1.0f);
284   }
285 
286   plo += FXSYS_roundf(newval);
287   decimal_helper_normalize(phi, pmid, plo);
288   m_uHi = static_cast<uint32_t>(phi);
289   m_uMid = static_cast<uint32_t>(pmid);
290   m_uLo = static_cast<uint32_t>(plo);
291   m_bNeg = val < 0 && IsNotZero();
292   m_uScale = scale;
293 }
294 
CFGAS_Decimal(WideStringView strObj)295 CFGAS_Decimal::CFGAS_Decimal(WideStringView strObj) {
296   const wchar_t* str = strObj.unterminated_c_str();
297   const wchar_t* strBound = str + strObj.GetLength();
298   bool pointmet = false;
299   bool negmet = false;
300   uint8_t scale = 0;
301   while (str != strBound && *str == ' ')
302     str++;
303   if (str != strBound && *str == '-') {
304     negmet = true;
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 = true;
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, false, 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