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