/[projects]/dao/DelphiScanner/Components/tpsystools_4.04/source/StDecMth.pas
ViewVC logotype

Contents of /dao/DelphiScanner/Components/tpsystools_4.04/source/StDecMth.pas

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2671 - (show annotations) (download)
Tue Aug 25 18:15:15 2015 UTC (8 years, 9 months ago) by torben
File size: 36514 byte(s)
Added tpsystools component
1 // Upgraded to Delphi 2009: Sebastian Zierer
2
3 (* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1
5 *
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
10 *
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
15 *
16 * The Original Code is TurboPower SysTools
17 *
18 * The Initial Developer of the Original Code is
19 * TurboPower Software
20 *
21 * Portions created by the Initial Developer are Copyright (C) 1996-2002
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 *
26 * ***** END LICENSE BLOCK ***** *)
27
28 {*********************************************************}
29 {* SysTools: StDecMth.pas 4.04 *}
30 {*********************************************************}
31 {* SysTools: Class for doing decimal arithmetic *}
32 {*********************************************************}
33
34 {$I StDefine.inc}
35
36 unit StDecMth;
37
38 interface
39
40 {Note: StDecMth declares and implements TStDecimal. This is a fixed-
41 point value with a total of 38 significant digits of which
42 16 are to the right of the decimal point.}
43
44 uses
45 SysUtils;
46
47 type
48 TStRoundMethod = ( {different rounding methods...}
49 rmNormal, {..normal (round away from zero if half way)}
50 rmTrunc, {..truncate (always round to zero)}
51 rmBankers, {..bankers (round to even digit if half way)}
52 rmUp); {..force round up (always round from zero)}
53
54 TStInt128 = array [0..3] of longint; // must be longint, not DWORD
55
56 TStDecimal = class
57 private
58 FInt : TStInt128;
59 protected
60 function dcGetAsStr : AnsiString;
61 procedure dcSetFromStr(const aValue : AnsiString); {!!.02}
62 public
63 constructor Create;
64 destructor Destroy; override;
65
66 function Compare(X : TStDecimal) : integer;
67 {-returns <0 if Self < X, 0 is equal, >0 otherwise}
68 function IsNegative : boolean;
69 {-returns Self < 0.0}
70 function IsOne : boolean;
71 {-returns Self = 1.0}
72 function IsPositive : boolean;
73 {-returns Self > 0.0}
74 function IsZero : boolean;
75 {-returns Self = 0.0}
76 procedure SetToOne;
77 {-sets Self equal to 1.0}
78 procedure SetToZero;
79 {-sets Self equal to 0.0}
80
81 procedure Assign(X : TStDecimal);
82 {-sets Self equal to X}
83 procedure AssignFromFloat(aValue : double);
84 {-sets Self equal to aValue}
85 procedure AssignFromInt(aValue : integer);
86 {-sets Self equal to aValue}
87
88 function AsFloat : double;
89 {-returns Self as an floating point value}
90 function AsInt(aRound : TStRoundMethod) : integer;
91 {-returns Self as an integer, rounded}
92
93 procedure Abs;
94 {-calculates Self := Abs(Self)}
95 procedure Add(X : TStDecimal);
96 {-calculates Self := Self + X}
97 procedure AddOne;
98 {-calculates Self := Self + 1.0}
99 procedure ChangeSign;
100 {-calculates Self := ChgSign(Self)}
101 procedure Divide(X : TStDecimal);
102 {-calculates Self := Self div X}
103 procedure Multiply(X : TStDecimal);
104 {-calculates Self := Self * X}
105 procedure RaiseToPower(N : integer);
106 {-calculates Self := Self ^ N}
107 procedure Round(aRound : TStRoundMethod; aDecPl : integer);
108 {-calculates Self := Round(Self)}
109 procedure Subtract(X : TStDecimal);
110 {-calculates Self := Self - X}
111 procedure SubtractOne;
112 {-calculates Self := Self - 1}
113
114 property AsString : AnsiString read dcGetAsStr write dcSetFromStr;
115 {-returns Self as a string, sets Self from a string}
116 end;
117
118 implementation
119
120 uses
121 StConst,
122 StBase;
123
124 type
125 TStInt256 = array [0..7] of integer;
126 TStInt192 = array [0..5] of integer;
127
128 const
129 MaxDecPl = 16;
130
131 Int128One_0 = longint($6FC10000);
132 Int128One_1 = longint($002386F2);
133
134 PowerOf10 : array [0..MaxDecPl div 2] of integer =
135 (1, 10, 100, 1000, 10000, 100000, 1000000, 10000000,
136 100000000);
137
138 {===Helper routines==================================================}
139 procedure Int256Div10E8(var X : TStInt256; var aRem : integer);
140 {Note: this routine assumes X is positive}
141 asm
142 push ebx // save ebx
143
144 push edx // save address of remainder variable
145
146 mov ecx, 100000000 // we're dividing by 10^8
147 mov ebx, eax // ebx points to X
148
149 xor edx, edx // start off with high dividend digit zero
150 mov eax, [ebx+28] // get last 32-bit digit
151 div ecx // divide by 10: eax is quotient, edx remainder
152 mov [ebx+28], eax // save highest quotient digit
153
154 mov eax, [ebx+24] // get next 32-bit digit
155 div ecx // divide by 10: eax is quotient, edx remainder
156 mov [ebx+24], eax // save next quotient digit
157
158 mov eax, [ebx+20] // get next 32-bit digit
159 div ecx // divide by 10: eax is quotient, edx remainder
160 mov [ebx+20], eax // save next quotient digit
161
162 mov eax, [ebx+16] // get next 32-bit digit
163 div ecx // divide by 10: eax is quotient, edx remainder
164 mov [ebx+16], eax // save next quotient digit
165
166 mov eax, [ebx+12] // get next 32-bit digit
167 div ecx // divide by 10: eax is quotient, edx remainder
168 mov [ebx+12], eax // save next quotient digit
169
170 mov eax, [ebx+8] // get next 32-bit digit
171 div ecx // divide by 10: eax is quotient, edx remainder
172 mov [ebx+8], eax // save next quotient digit
173
174 mov eax, [ebx+4] // get next 32-bit digit
175 div ecx // divide by 10: eax is quotient, edx remainder
176 mov [ebx+4], eax // save next quotient digit
177
178 mov eax, [ebx] // get first 32-bit digit
179 div ecx // divide by 10: eax is quotient, edx remainder
180 mov [ebx], eax // save first quotient digit
181
182 pop eax // pop off the address of remainder variable
183 mov [eax], edx // store remainder
184
185 pop ebx // restore ebx
186 end;
187 {--------}
188 procedure Int192Times10E8(var X : TStInt192);
189 {Note: this routine assumes X is positive}
190 asm
191 push ebx // save ebx
192 push ebp // save ebp
193
194 mov ecx, 100000000 // we're multiplying by 10^8
195 mov ebx, eax // ebx points to X
196
197 mov eax, [ebx] // get the first 32-bit digit
198 mul ecx // multiply it by 10^8 to give answer in edx:eax
199 mov [ebx], eax // save first digit of result
200 mov ebp, edx // save overflow
201
202 mov eax, [ebx+4] // get the second 32-bit digit
203 mul ecx // multiply it by 10^8 to give answer in edx:eax
204 add eax, ebp // add the overflow from the first digit
205 adc edx, 0
206 mov [ebx+4], eax // save second digit of result
207 mov ebp, edx // save overflow
208
209 mov eax, [ebx+8] // get the third 32-bit digit
210 mul ecx // multiply it by 10^8 to give answer in edx:eax
211 add eax, ebp // add the overflow from the second digit
212 adc edx, 0
213 mov [ebx+8], eax // save third digit of result
214 mov ebp, edx // save overflow
215
216 mov eax, [ebx+12] // get the fourth 32-bit digit
217 mul ecx // multiply it by 10^8 to give answer in edx:eax
218 add eax, ebp // add the overflow from the third digit
219 adc edx, 0
220 mov [ebx+12], eax // save fourth digit of result
221 mov ebp, edx // save overflow
222
223 mov eax, [ebx+16] // get the fifth 32-bit digit
224 mul ecx // multiply it by 10^8 to give answer in edx:eax
225 add eax, ebp // add the overflow from the fourth digit
226 adc edx, 0
227 mov [ebx+16], eax // save fifth digit of result
228 mov ebp, edx // save overflow
229
230 mov eax, [ebx+20] // get the sixth 32-bit digit
231 mul ecx // multiply it by 10^8 to give answer in edx:eax
232 add eax, ebp // add the overflow from the fifth digit
233 mov [ebx+20], eax // save sixth digit of result
234
235 pop ebp // restore ebp
236 pop ebx // restore ebx
237 end;
238 {--------}
239 function Int32MultPrim(X, Y : longint;
240 var P : longint; Carry : longint) : longint;
241 asm
242 {Note: calculates X * Y + P + Carry
243 returns answer in P, with overflow as result value}
244 mul edx
245 add eax, [ecx]
246 adc edx, 0
247 add eax, Carry
248 adc edx, 0
249 mov [ecx], eax
250 mov eax, edx
251 end;
252 {--------}
253 procedure Int128Add(var X : TStInt128; const Y : TStInt128);
254 asm
255 push ebx
256 mov ecx, [edx]
257 mov ebx, [edx+4]
258 add [eax], ecx
259 adc [eax+4], ebx
260 mov ecx, [edx+8]
261 mov ebx, [edx+12]
262 adc [eax+8], ecx
263 adc [eax+12], ebx
264 pop ebx
265 end;
266 {--------}
267 procedure Int128AddInt(var X : TStInt128; aDigit : integer);
268 asm
269 add [eax], edx
270 adc dword ptr [eax+4], 0
271 adc dword ptr [eax+8], 0
272 adc dword ptr [eax+12], 0
273 end;
274 {--------}
275 procedure Int128ChgSign(var X : TStInt128);
276 asm
277 mov ecx, [eax]
278 mov edx, [eax+4]
279 not ecx
280 not edx
281 add ecx, 1
282 adc edx, 0
283 mov [eax], ecx
284 mov [eax+4], edx
285 mov ecx, [eax+8]
286 mov edx, [eax+12]
287 not ecx
288 not edx
289 adc ecx, 0
290 adc edx, 0
291 mov [eax+8], ecx
292 mov [eax+12], edx
293 end;
294 {--------}
295 function Int128Compare(const X, Y : TStInt128) : integer;
296 asm
297 // Can be called from pascal
298 // All registers are preserved, except eax, which returns the
299 // result of the comparison
300 push ebx
301 push ecx
302 mov ecx, [eax+12]
303 mov ebx, [edx+12]
304 xor ecx, $80000000
305 xor ebx, $80000000
306 cmp ecx, ebx
307 jb @@LessThan
308 ja @@GreaterThan
309 mov ecx, [eax+8]
310 mov ebx, [edx+8]
311 cmp ecx, ebx
312 jb @@LessThan
313 ja @@GreaterThan
314 mov ecx, [eax+4]
315 mov ebx, [edx+4]
316 cmp ecx, ebx
317 jb @@LessThan
318 ja @@GreaterThan
319 mov ecx, [eax]
320 mov ebx, [edx]
321 cmp ecx, ebx
322 jb @@LessThan
323 ja @@GreaterThan
324 xor eax, eax
325 jmp @@Exit
326 @@LessThan:
327 mov eax, -1
328 jmp @@Exit
329 @@GreaterThan:
330 mov eax, 1
331 @@Exit:
332 pop ecx
333 pop ebx
334 end;
335 {--------}
336 procedure Int192SHL(var X : TStInt192);
337 asm
338 // DO NOT CALL FROM PASCAL
339 // IN: eax -> 192-bit integer to shift left
340 // OUT: eax -> 192-bit integer shifted left
341 // CF = most significant bit shifted out
342 // All registers are preserved
343 push ebx
344 push ecx
345 mov ebx, [eax]
346 mov ecx, [eax+4]
347 shl ebx, 1
348 rcl ecx, 1
349 mov [eax], ebx
350 mov [eax+4], ecx
351 mov ebx, [eax+8]
352 mov ecx, [eax+12]
353 rcl ebx, 1
354 rcl ecx, 1
355 mov [eax+8], ebx
356 mov [eax+12], ecx
357 mov ebx, [eax+16]
358 mov ecx, [eax+20]
359 rcl ebx, 1
360 rcl ecx, 1
361 mov [eax+16], ebx
362 mov [eax+20], ecx
363 pop ecx
364 pop ebx
365 end;
366 {--------}
367 procedure Int128RCL(var X : TStInt128);
368 asm
369 // DO NOT CALL FROM PASCAL
370 // IN: eax -> 128-bit integer to shift left
371 // CF = least significant bit to shift in
372 // OUT: eax -> 128-bit integer shifted left
373 // CF -> topmost bit shifted out
374 // All registers are preserved
375 push ebx
376 push ecx
377 mov ebx, [eax]
378 mov ecx, [eax+4]
379 rcl ebx, 1
380 rcl ecx, 1
381 mov [eax], ebx
382 mov [eax+4], ecx
383 mov ebx, [eax+8]
384 mov ecx, [eax+12]
385 rcl ebx, 1
386 rcl ecx, 1
387 mov [eax+8], ebx
388 mov [eax+12], ecx
389 pop ecx
390 pop ebx
391 end;
392 {--------}
393 procedure Int128FastDivide(var X : TStInt192;
394 var Y, aRem : TStInt128);
395 asm
396 push ebp
397 push ebx
398 push edi
399 push esi
400
401 mov esi, eax // esi -> dividend
402 mov edi, edx // edi -> divisor
403 mov ebp, ecx // ebp -> remainder
404
405 mov ecx, 192 // we'll do the loop for all 192 bits in the
406 // dividend
407
408 xor eax, eax // zero the remainder
409 mov [ebp], eax
410 mov [ebp+4], eax
411 mov [ebp+8], eax
412 mov [ebp+12], eax
413
414 @@GetNextBit:
415 mov eax, esi // shift the dividend left, and...
416 call Int192SHL
417 mov eax, ebp // ...shift the topmost bit into the remainder
418 call Int128RCL
419
420 mov eax, ebp // compare the remainder with the divisor
421 mov edx, edi
422 call Int128Compare
423
424 cmp eax, 0 // if the remainder is smaller, we can't
425 jl @@TooSmall // subtract the divisor
426
427 // essentially we've shown that the divisor
428 // divides the remainder exactly once, so
429
430 add dword ptr [esi], 1 // add one to the quotient
431
432 mov eax, [ebp] // subtract the divisor from the remainder
433 mov ebx, [ebp+4]
434 sub eax, [edi]
435 sbb ebx, [edi+4]
436 mov [ebp], eax
437 mov [ebp+4], ebx
438 mov eax, [ebp+8]
439 mov ebx, [ebp+12]
440 sbb eax, [edi+8]
441 sbb ebx, [edi+12]
442 mov [ebp+8], eax
443 mov [ebp+12], ebx
444
445 @@TooSmall:
446 dec ecx // go get the next bit to work on
447 jnz @@GetNextBit
448
449 pop esi
450 pop edi
451 pop ebx
452 pop ebp
453 end;
454 {--------}
455 function Int128DivInt(var X : TStInt128; aDivisor : integer) : integer;
456 {Note: this routine assumes X is positive}
457 asm
458 push ebx // save ebx
459
460 mov ecx, edx // ecx is now the divisor
461 mov ebx, eax // ebx points to X
462
463 xor edx, edx // start off with high dividend digit zero
464 mov eax, [ebx+12] // get last 32-bit digit
465 div ecx // divide by ecx: eax is quotient, edx remainder
466 mov [ebx+12], eax // save highest quotient digit
467
468 mov eax, [ebx+8] // get next 32-bit digit
469 div ecx // divide by ecx: eax is quotient, edx remainder
470 mov [ebx+8], eax // save next quotient digit
471
472 mov eax, [ebx+4] // get next 32-bit digit
473 div ecx // divide by ecx: eax is quotient, edx remainder
474 mov [ebx+4], eax // save next quotient digit
475
476 mov eax, [ebx] // get first 32-bit digit
477 div ecx // divide by ecx: eax is quotient, edx remainder
478 mov [ebx], eax // save first quotient digit
479
480 mov eax, edx // return remainder
481
482 pop ebx // restore ebx
483 end;
484 {--------}
485 procedure Int128Divide(var X, Y : TStInt128);
486 var
487 XTemp : TStInt192;
488 Rem : TStInt128;
489 begin
490 {note: the easy cases have been dealt with
491 X and Y are both positive
492 X will be set to the quotient X/Y and Y will be trashed}
493
494 {we need to increase the number of decimal places to 32, so convert
495 the 128 bit dividend to a 192 bit one and multiply by 10^16}
496 XTemp[0] := X[0];
497 XTemp[1] := X[1];
498 XTemp[2] := X[2];
499 XTemp[3] := X[3];
500 XTemp[4] := 0;
501 XTemp[5] := 0;
502 Int192Times10E8(XTemp);
503 Int192Times10E8(XTemp);
504
505 {Note: this algorithm follows that described by Knuth in volume 2 of
506 The Art of Computer Programming. Algorithm D of section 4.3
507 as applied to binary numbers (radix=2)}
508
509 {divide the 192-bit dividend by the 128-bit divisor}
510 Int128FastDivide(XTemp, Y, Rem);
511
512 {have we overflowed? ie, have we divided a very big number by one
513 much less than zero}
514 if (XTemp[3] < 0) or (XTemp[4] <> 0) or (XTemp[5] <> 0) then
515 raise EStDecMathError.Create(stscDecMathDivOverflowS);
516
517 {return the result of the computation}
518 X[0] := XTemp[0];
519 X[1] := XTemp[1];
520 X[2] := XTemp[2];
521 X[3] := XTemp[3];
522 end;
523 {--------}
524 procedure Int128Multiply(var X, Y : TStInt128);
525 var
526 P : TStInt256;
527 XIsNeg : boolean;
528 YIsNeg : boolean;
529 YInx : integer;
530 YDigit : integer;
531 Carry : integer;
532 YTemp : TStInt128;
533 begin
534 {Note: calculates X * Y and puts the answer in X}
535
536 {get rid of the easy cases where one of the operands is zero}
537 if (X[0] = 0) and (X[1] = 0) and (X[2] = 0) and (X[3] = 0) then
538 Exit;
539 if (Y[0] = 0) and (Y[1] = 0) and (Y[2] = 0) and (Y[3] = 0) then begin
540 X[0] := 0;
541 X[1] := 0;
542 X[2] := 0;
543 X[3] := 0;
544 Exit;
545 end;
546
547 {we might need to trash Y, so we use a local variable}
548 YTemp[0] := Y[0];
549 YTemp[1] := Y[1];
550 YTemp[2] := Y[2];
551 YTemp[3] := Y[3];
552
553 {convert both operands to positive values: we'll fix the sign later}
554 XIsNeg := X[3] < 0;
555 if XIsNeg then
556 Int128ChgSign(X);
557 YIsNeg := YTemp[3] < 0;
558 if YIsNeg then
559 Int128ChgSign(YTemp);
560
561 {initialize the temporary product}
562 P[0] := 0;
563 P[1] := 0;
564 P[2] := 0;
565 P[3] := 0;
566 P[4] := 0;
567 P[5] := 0;
568 P[6] := 0;
569 P[7] := 0;
570
571 {for every digit in Y we shall multiply by all the X digits and sum}
572 for YInx := 0 to 3 do begin
573
574 {get the Y digit}
575 YDigit := YTemp[YInx];
576
577 {there's only something to do if the Y digit is non-zero}
578 if (YDigit <> 0) then begin
579
580 {multiply this digit with all the X digits, storing the result
581 in the temporary product}
582 Carry := Int32MultPrim(X[0], YDigit, P[YInx], 0);
583 Carry := Int32MultPrim(X[1], YDigit, P[YInx + 1], Carry);
584 Carry := Int32MultPrim(X[2], YDigit, P[YInx + 2], Carry);
585 P[YInx + 4] := Int32MultPrim(X[3], YDigit, P[YInx + 3], Carry);
586 end;
587 end;
588
589 {the product has 32 decimal places, so divide by 10^8 twice to get
590 the answer to the 16 decimal places we need}
591 Int256Div10E8(P, Carry);
592 Int256Div10E8(P, Carry);
593
594 {note: if Carry <> 0 then we're losing precision}
595
596 {check for multiplication overflow}
597 if (P[3] < 0) or
598 (P[4] <> 0) or (P[5] <> 0) or (P[6] <> 0) or (P[7] <> 0) then
599 raise EStDecMathError.Create(stscDecMathMultOverflowS);
600
601 {return the value in X, remembering to set the sign}
602 X[0] := P[0];
603 X[1] := P[1];
604 X[2] := P[2];
605 X[3] := P[3];
606
607 (*
608 {round if necessary}
609 if (Carry >= 500000000) then
610 Int128AddInt(X, 1);
611 *)
612
613 {set the sign}
614 if (XIsNeg xor YIsNeg) then
615 Int128ChgSign(X);
616 end;
617 {--------}
618 procedure Int128TimesInt(var X : TStInt128; aValue : integer);
619 {Note: this routine assumes X is positive}
620 asm
621 push ebx // save ebx
622 push ebp // save ebp
623
624 mov ecx, edx // we're multiplying by aValue
625 mov ebx, eax // ebx points to X
626
627 mov eax, [ebx] // get the first 32-bit digit
628 mul ecx // multiply it by 10 to give answer in edx:eax
629 mov [ebx], eax // save first digit of result
630 mov ebp, edx // save overflow
631
632 mov eax, [ebx+4] // get the second 32-bit digit
633 mul ecx // multiply it by 10 to give answer in edx:eax
634 add eax, ebp // add the overflow from the first digit
635 adc edx, 0
636 mov [ebx+4], eax // save second digit of result
637 mov ebp, edx // save overflow
638
639 mov eax, [ebx+8] // get the third 32-bit digit
640 mul ecx // multiply it by 10 to give answer in edx:eax
641 add eax, ebp // add the overflow from the second digit
642 adc edx, 0
643 mov [ebx+8], eax // save second digit of result
644 mov ebp, edx // save overflow
645
646 mov eax, [ebx+12] // get the third 32-bit digit
647 mul ecx // multiply it by 10 to give answer in edx:eax
648 add eax, ebp // add the overflow from the second digit
649 mov [ebx+12], eax // save third digit of result
650
651 pop ebp // restore ebp
652 pop ebx // restore ebx
653 end;
654 {--------}
655 procedure Int128Round(var X : TStInt128;
656 aRound : TStRoundMethod;
657 aDecPl : integer);
658 var
659 Rem : integer;
660 HadRem : boolean;
661 AddOne : boolean;
662 Expnt : integer;
663 NeedInt : boolean;
664 begin
665 {Assumptions: X is positive, 0 <= aDecPl <= MaxDecPl
666 --the caller *must* ensure these}
667
668 {if the number of decimal places is -1, it's a special signal to
669 perform the rounding to an integer, but not to multiply the result
670 by 10^16 at the end; the caller is AsInt, in other words}
671 if (aDecPl >= 0) then
672 NeedInt := false
673 else begin
674 NeedInt := true;
675 aDecPl := 0;
676 end;
677
678 {if we're asked to round to the precision of the type, there's
679 nothing to do}
680 if (aDecPl = MaxDecPl) then
681 Exit;
682
683 {perform the required rounding}
684 AddOne := false; // keep the compiler happy
685 case aRound of
686 rmNormal :
687 begin
688 {to do normal rounding: divide by the required power of ten,
689 if the most significant digit of the remainder was 5 or more,
690 we'll add one to the result}
691 Expnt := MaxDecPl - aDecPl - 1;
692 if (Expnt > 0) then begin
693 if (Expnt > 8) then begin
694 Int128DivInt(X, PowerOf10[8]);
695 dec(Expnt, 8);
696 end;
697 Int128DivInt(X, PowerOf10[Expnt]);
698 end;
699 AddOne := Int128DivInt(X, 10) >= 5;
700 end;
701 rmTrunc :
702 begin
703 {to truncate: just divide by the required power of ten}
704 Expnt := MaxDecPl - aDecPl;
705 if (Expnt > 8) then begin
706 Int128DivInt(X, PowerOf10[8]);
707 dec(Expnt, 8);
708 end;
709 Int128DivInt(X, PowerOf10[Expnt]);
710 AddOne := false;
711 end;
712 rmBankers :
713 begin
714 {to do bankers rounding:
715 - divide by the required power of ten, checking to see if
716 there's a non-zero remainder
717 - if the most significant digit of the remainder was greater
718 than 5, we'll add one to the result
719 - if the most significant digit of the remainder was 5 and
720 there was at least one other digit in the remainder, we'll
721 add one to the result
722 - if the most significant digit of the remainder was 5 and
723 there were no other digits in the remainder, determine if
724 the result is odd; if it is, we'll add one to the result}
725 HadRem := false;
726 if ((MaxDecPl - aDecPl) > 1) then begin
727 Expnt := MaxDecPl - aDecPl - 1;
728 if (Expnt > 8) then begin
729 if (Int128DivInt(X, PowerOf10[8]) <> 0) then
730 HadRem := true;
731 dec(Expnt, 8);
732 end;
733 if (Int128DivInt(X, PowerOf10[Expnt]) <> 0) then
734 HadRem := true;
735 end;
736 Rem := Int128DivInt(X, 10);
737 AddOne := (Rem > 5) or
738 ((Rem = 5) and HadRem) or
739 ((Rem = 5) and Odd(X[0]));
740 end;
741 rmUp :
742 begin
743 {to always round up: divide by the required power of ten,
744 if there was a remainder, we'll add one to the result}
745 AddOne := false;
746 Expnt := MaxDecPl - aDecPl;
747 if (Expnt > 8) then begin
748 if (Int128DivInt(X, PowerOf10[8]) <> 0) then
749 AddOne := true;
750 dec(Expnt, 8);
751 end;
752 if (Int128DivInt(X, PowerOf10[Expnt]) <> 0) then
753 AddOne := true;
754 end;
755 end;{case}
756
757 {add one to the result, if required}
758 if AddOne then
759 Int128AddInt(X, 1);
760
761 {finally, multiply by the required power of ten}
762 if not NeedInt then begin
763 Expnt := MaxDecPl - aDecPl;
764 if (Expnt > 8) then begin
765 Int128TimesInt(X, PowerOf10[8]);
766 dec(Expnt, 8);
767 end;
768 Int128TimesInt(X, PowerOf10[Expnt]);
769 end;
770 end;
771 {====================================================================}
772
773
774 {====================================================================}
775 constructor TStDecimal.Create;
776 begin
777 {create the ancestor}
778 inherited Create;
779 {note: the internal number will be automatically zero}
780 end;
781 {--------}
782 destructor TStDecimal.Destroy;
783 begin
784 {free the ancestor}
785 inherited Destroy;
786 end;
787 {--------}
788 procedure TStDecimal.Abs;
789 begin
790 if (FInt[3] < 0) then
791 Int128ChgSign(FInt);
792 end;
793 {--------}
794 procedure TStDecimal.Add(X : TStDecimal);
795 begin
796 if (X <> nil) then
797 Int128Add(FInt, X.FInt);
798 end;
799 {--------}
800 procedure TStDecimal.AddOne;
801 var
802 One : TStInt128;
803 begin
804 One[0] := Int128One_0;
805 One[1] := Int128One_1;
806 One[2] := 0;
807 One[3] := 0;
808 Int128Add(FInt, One);
809 end;
810 {--------}
811 function TStDecimal.AsFloat : double;
812 begin
813 Result := StrToFloat(AsString);
814 end;
815 {--------}
816 function TStDecimal.AsInt(aRound : TStRoundMethod) : integer;
817 var
818 X : TStInt128;
819 IsNeg : boolean;
820 begin
821 {get the current value locally}
822 X[0] := FInt[0];
823 X[1] := FInt[1];
824 X[2] := FInt[2];
825 X[3] := FInt[3];
826
827 {force it to be positive}
828 IsNeg := X[3] < 0;
829 if IsNeg then
830 Int128ChgSign(X);
831
832 {round it to an integer}
833 Int128Round(X, aRound, -1);
834
835 {check for errors (the least significant digit cannot be negative,
836 and all the others must be zero)}
837 if (X[0] < 0) or (X[1] <> 0) or (X[2] <> 0) or (X[3] <> 0) then
838 raise EStDecMathError.Create(stscDecMathAsIntOverflowS);
839
840 {return the result}
841 if IsNeg then
842 Result := -X[0]
843 else
844 Result := X[0];
845 end;
846 {--------}
847 procedure TStDecimal.Assign(X : TStDecimal);
848 begin
849 if (X = nil) then
850 SetToZero
851 else begin
852 FInt[0] := X.FInt[0];
853 FInt[1] := X.FInt[1];
854 FInt[2] := X.FInt[2];
855 FInt[3] := X.FInt[3];
856 end;
857 end;
858 {--------}
859 procedure TStDecimal.AssignFromFloat(aValue : double);
860 begin
861 AsString := Format('%38.16f', [aValue]);
862 end;
863 {--------}
864 procedure TStDecimal.AssignFromInt(aValue : integer);
865 begin
866 FInt[0] := System.Abs(aValue);
867 FInt[1] := 0;
868 FInt[2] := 0;
869 FInt[3] := 0;
870 Int128TimesInt(FInt, PowerOf10[8]);
871 Int128TimesInt(FInt, PowerOf10[8]);
872 if (aValue < 0) then
873 Int128ChgSign(FInt);
874 end;
875 {--------}
876 procedure TStDecimal.ChangeSign;
877 begin
878 Int128ChgSign(FInt);
879 end;
880 {--------}
881 function TStDecimal.Compare(X : TStDecimal) : integer;
882 begin
883 Compare := Int128Compare(FInt, X.FInt);
884 end;
885 {--------}
886 function TStDecimal.dcGetAsStr : AnsiString;
887 var
888 X : TStInt128;
889 i : integer;
890 Rem : integer;
891 IsNeg : boolean;
892 ChStack: array [0..47] of AnsiChar;
893 // this is ample for 38 digits + punctuation
894 ChSP : integer;
895 begin
896 {initialize the stack}
897 ChSP := 0;
898
899 {since we're going to trash the value, store it locally}
900 X[0] := FInt[0];
901 X[1] := FInt[1];
902 X[2] := FInt[2];
903 X[3] := FInt[3];
904
905 {make sure it's positive}
906 IsNeg := X[3] < 0;
907 if IsNeg then
908 Int128ChgSign(X);
909
910 {push the least significant digits (those that will appear after the
911 radix point)}
912 for i := 1 to MaxDecPl do begin
913 Rem := Int128DivInt(X, 10);
914 ChStack[ChSP] := AnsiChar(Rem + ord('0'));
915 inc(ChSP);
916 end;
917
918 {push the radix point}
919 ChStack[ChSP] := AnsiChar({$IFDEF DELPHIXE2}FormatSettings.{$ENDIF}DecimalSeparator);
920 inc(ChSP);
921
922 {repeat until the local value is zero}
923 repeat
924 Rem := Int128DivInt(X, 10);
925 ChStack[ChSP] := AnsiChar(Rem + ord('0'));
926 inc(ChSP);
927 until (X[0] = 0) and (X[1] = 0) and (X[2] = 0) and (X[3] = 0);
928
929 {if the value was negative, push a minus sign}
930 if IsNeg then begin
931 ChStack[ChSP] := '-';
932 inc(ChSP);
933 end;
934
935 {construct the result value by popping off characters}
936 SetLength(Result, ChSP);
937 i := 1;
938 while (ChSP <> 0) do begin
939 dec(ChSP);
940 Result[i] := ChStack[ChSP];
941 inc(i);
942 end;
943 end;
944 {--------}
945 procedure TStDecimal.dcSetFromStr(const aValue : AnsiString); {!!.02}
946 var
947 State : (ScanStart, ScanSign, ScanRadix, ScanBefore,
948 ScanAfter, ScanEnd, GotError);
949 i : integer;
950 Ch : AnsiChar;
951 IsNeg : boolean;
952 DecPlCount : integer;
953 begin
954 {Note: this implements the following DFA:
955
956 ScanStart --space--> ScanStart
957 ScanStart --plus---> ScanSign
958 ScanStart --minus--> ScanSign
959 ScanStart --digit--> ScanBefore
960 ScanStart --radix--> ScanRadix
961
962 ScanSign --radix--> ScanRadix
963 ScanSign --digit--> ScanBefore
964
965 ScanRadix --digit--> ScanAfter
966
967 ScanBefore --radix--> ScanAfter
968 ScanBefore --digit--> ScanBefore
969 ScanBefore --space--> ScanEnd
970
971 ScanAfter --digit--> ScanAfter
972 ScanAfter --space--> ScanEnd
973
974 ScanEnd --space--> ScanEnd
975
976 The terminating states are ScanBefore, ScanAfter and ScanEnd; in
977 other words, a valid numeric string cannot end in a radix point.
978 }
979
980 {initialize}
981 SetToZero;
982 DecPlCount := 0;
983 IsNeg := false;
984 State := ScanStart;
985
986 {read through the input string}
987 for i := 1 to length(aValue) do begin
988
989 {get the current character}
990 Ch := aValue[i];
991
992 case State of
993 ScanStart :
994 begin
995 if ('0' <= Ch) and (Ch <= '9') then begin
996 FInt[0] := ord(Ch) - ord('0');
997 State := ScanBefore;
998 end
999 else if (Ch = '+') then begin
1000 State := ScanSign;
1001 end
1002 else if (Ch = '-') then begin
1003 IsNeg := true;
1004 State := ScanSign;
1005 end
1006 else if (Ch = AnsiChar({$IFDEF DELPHIXE2}FormatSettings.{$ENDIF}DecimalSeparator)) then begin
1007 State := ScanRadix;
1008 end
1009 else if (Ch <> ' ') then
1010 State := GotError;
1011 end;
1012 ScanSign :
1013 begin
1014 if ('0' <= Ch) and (Ch <= '9') then begin
1015 FInt[0] := ord(Ch) - ord('0');
1016 State := ScanBefore;
1017 end
1018 else if (Ch = AnsiChar({$IFDEF DELPHIXE2}FormatSettings.{$ENDIF}DecimalSeparator)) then begin
1019 State := ScanRadix;
1020 end
1021 else
1022 State := GotError;
1023 end;
1024 ScanRadix :
1025 begin
1026 if ('0' <= Ch) and (Ch <= '9') then begin
1027 inc(DecPlCount);
1028 Int128TimesInt(FInt, 10);
1029 Int128AddInt(FInt, ord(Ch) - ord('0'));
1030 State := ScanAfter;
1031 end
1032 else
1033 State := GotError;
1034 end;
1035 ScanBefore :
1036 begin
1037 if ('0' <= Ch) and (Ch <= '9') then begin
1038 Int128TimesInt(FInt, 10);
1039 Int128AddInt(FInt, ord(Ch) - ord('0'));
1040 end
1041 else if (Ch = AnsiChar({$IFDEF DELPHIXE2}FormatSettings.{$ENDIF}DecimalSeparator)) then begin
1042 State := ScanAfter;
1043 end
1044 else if (Ch = ' ') then
1045 State := ScanEnd
1046 else
1047 State := GotError;
1048 end;
1049 ScanAfter :
1050 begin
1051 if ('0' <= Ch) and (Ch <= '9') then begin
1052 inc(DecPlCount);
1053 if (DecPlCount <= MaxDecPl) then begin
1054 Int128TimesInt(FInt, 10);
1055 Int128AddInt(FInt, ord(Ch) - ord('0'));
1056 end;
1057 end
1058 else if (Ch = ' ') then
1059 State := ScanEnd
1060 else
1061 State := GotError;
1062 end;
1063 ScanEnd :
1064 begin
1065 if (Ch <> ' ') then
1066 State := GotError;
1067 end;
1068 GotError :
1069 begin
1070 Break;
1071 end;
1072 end;
1073 end;
1074
1075 if (State <> ScanBefore) and
1076 (State <> ScanAfter) and
1077 (State <> ScanEnd) then
1078 raise EStDecMathError.Create(stscDecMathConversionS);
1079
1080 {make sure we have the correct number of decimal places}
1081 if (MaxDecPl > DecPlCount) then begin
1082 DecPlCount := MaxDecPl - DecPlCount;
1083 if (DecPlCount > 8) then begin
1084 Int128TimesInt(FInt, Powerof10[8]);
1085 dec(DecPlCount, 8);
1086 end;
1087 Int128TimesInt(FInt, Powerof10[DecPlCount]);
1088 end;
1089
1090 {force negative, if required}
1091 if IsNeg then
1092 Int128ChgSign(FInt);
1093 end;
1094 {--------}
1095 procedure TStDecimal.Divide(X : TStDecimal);
1096 var
1097 TempX : TStInt128;
1098 IsNeg : boolean;
1099 XIsNeg : boolean;
1100 begin
1101 {easy case: X is nil or zero}
1102 if (X = nil) or X.IsZero then
1103 raise EStDecMathError.Create(stscDecMathDivByZeroS);
1104
1105 {easy case: Self is zero}
1106 if IsZero then
1107 Exit;
1108
1109 {we might have to change X, so make it local}
1110 TempX[0] := X.FInt[0];
1111 TempX[1] := X.FInt[1];
1112 TempX[2] := X.FInt[2];
1113 TempX[3] := X.FInt[3];
1114
1115 {force the divisor and dividend positive}
1116 IsNeg := FInt[3] < 0;
1117 if IsNeg then
1118 Int128ChgSign(FInt);
1119 XIsNeg := TempX[3] < 0;
1120 if XIsNeg then
1121 Int128ChgSign(TempX);
1122
1123 {easy case: X is 1.0: set the correct sign}
1124 if (TempX[0] = Int128One_0) and (TempX[1] = Int128One_1) and
1125 (TempX[2] = 0) and (TempX[3] = 0) then begin
1126 if (IsNeg xor XIsNeg) then
1127 Int128ChgSign(FInt);
1128 Exit;
1129 end;
1130
1131 {easy case: compare the dividend and divisor: if they're equal,
1132 set ourselves to 1.0 with the correct sign}
1133 if (Int128Compare(FInt, TempX) = 0) then begin
1134 FInt[0] := Int128One_0;
1135 FInt[1] := Int128One_1;
1136 FInt[2] := 0;
1137 FInt[3] := 0;
1138 if (IsNeg xor XIsNeg) then
1139 Int128ChgSign(FInt);
1140 Exit;
1141 end;
1142
1143 {no more easy cases: just do the division}
1144 Int128Divide(FInt, TempX);
1145
1146 {set the sign}
1147 if (IsNeg xor XIsNeg) then
1148 Int128ChgSign(FInt);
1149 end;
1150 {--------}
1151 function TStDecimal.IsNegative : boolean;
1152 begin
1153 {if the most significant longint is negative, so is the value}
1154 Result := FInt[3] < 0;
1155 end;
1156 {--------}
1157 function TStDecimal.IsOne : boolean;
1158 begin
1159 Result := (FInt[0] = Int128One_0) and (FInt[1] = Int128One_1) and
1160 (FInt[2] = 0) and (FInt[3] = 0);
1161 end;
1162 {--------}
1163 function TStDecimal.IsPositive : boolean;
1164 begin
1165 {if the most significant longint is positive, so is the value; if it
1166 is zero, one of the other longints must be non-zero for the value
1167 to be positive}
1168 Result := (FInt[3] > 0) or
1169 ((FInt[3] = 0) and
1170 ((FInt[2] <> 0) or (FInt[1] <> 0) or (FInt[0] <> 0)));
1171 end;
1172 {--------}
1173 function TStDecimal.IsZero : boolean;
1174 begin
1175 Result := (FInt[0] = 0) and (FInt[1] = 0) and
1176 (FInt[2] = 0) and (FInt[3] = 0);
1177 end;
1178 {--------}
1179 procedure TStDecimal.Multiply(X : TStDecimal);
1180 begin
1181 if (X = nil) then
1182 SetToZero
1183 else
1184 Int128Multiply(FInt, X.FInt);
1185 end;
1186 {--------}
1187 procedure TStDecimal.RaiseToPower(N : integer);
1188 var
1189 Accum : TStInt128;
1190 Mask : longint;
1191 IsNeg : boolean;
1192 begin
1193 {take care of some easy cases}
1194 if (N < 0) then
1195 raise EStDecMathError.Create(stscDecMathNegExpS);
1196 if (N = 0) then begin
1197 SetToOne;
1198 Exit;
1199 end;
1200 if (N = 1) then
1201 Exit;
1202
1203 {force the value positive}
1204 IsNeg := FInt[3] < 0;
1205 if IsNeg then
1206 Int128ChgSign(FInt);
1207
1208 {initialize the accumulator to 1.0}
1209 Accum[0] := Int128One_0;
1210 Accum[1] := Int128One_1;
1211 Accum[2] := 0;
1212 Accum[3] := 0;
1213
1214 {set the bit mask}
1215 Mask := longint($80000000);
1216
1217 {find the first set bit in the exponent}
1218 while ((N and Mask) = 0) do
1219 Mask := Mask shr 1;
1220
1221 {calculate the power}
1222 while (Mask <> 0) do begin
1223 Int128Multiply(Accum, Accum);
1224 if ((N and Mask) <> 0) then
1225 Int128Multiply(Accum, FInt);
1226 Mask := Mask shr 1;
1227 end;
1228
1229 {save the calculated value}
1230 FInt[0] := Accum[0];
1231 FInt[1] := Accum[1];
1232 FInt[2] := Accum[2];
1233 FInt[3] := Accum[3];
1234
1235 {force the value negative if required}
1236 if IsNeg and Odd(N) then
1237 Int128ChgSign(FInt);
1238 end;
1239 {--------}
1240 procedure TStDecimal.Round(aRound : TStRoundMethod; aDecPl : integer);
1241 var
1242 IsNeg : boolean;
1243 begin
1244 {check decimal places parameter to be in range}
1245 if not ((0 <= aDecPl) and (aDecPl <= MaxDecPl)) then
1246 raise EStDecMathError.Create(stscDecMathRoundPlacesS);
1247
1248 {force the value positive}
1249 IsNeg := FInt[3] < 0;
1250 if IsNeg then
1251 Int128ChgSign(FInt);
1252
1253 {perform the rounding}
1254 Int128Round(FInt, aRound, aDecPl);
1255
1256 {force the value negative if it was negative}
1257 if IsNeg then
1258 Int128ChgSign(FInt);
1259 end;
1260 {--------}
1261 procedure TStDecimal.SetToOne;
1262 begin
1263 FInt[0] := Int128One_0;
1264 FInt[1] := Int128One_1;
1265 FInt[2] := 0;
1266 FInt[3] := 0;
1267 end;
1268 {--------}
1269 procedure TStDecimal.SetToZero;
1270 begin
1271 FInt[0] := 0;
1272 FInt[1] := 0;
1273 FInt[2] := 0;
1274 FInt[3] := 0;
1275 end;
1276 {--------}
1277 procedure TStDecimal.Subtract(X : TStDecimal);
1278 var
1279 MinusX : TStInt128;
1280 begin
1281 if (X <> nil) then begin
1282 MinusX[0] := X.FInt[0];
1283 MinusX[1] := X.FInt[1];
1284 MinusX[2] := X.FInt[2];
1285 MinusX[3] := X.FInt[3];
1286 Int128ChgSign(MinusX);
1287 Int128Add(Fint, MinusX);
1288 end;
1289 end;
1290 {--------}
1291 procedure TStDecimal.SubtractOne;
1292 var
1293 MinusOne : TStInt128;
1294 begin
1295 MinusOne[0] := Int128One_0;
1296 MinusOne[1] := Int128One_1;
1297 MinusOne[2] := 0;
1298 MinusOne[3] := 0;
1299 Int128ChgSign(MinusOne);
1300 Int128Add(FInt, MinusOne);
1301 end;
1302 {====================================================================}
1303
1304 end.

  ViewVC Help
Powered by ViewVC 1.1.20