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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2671 - (hide annotations) (download)
Tue Aug 25 18:15:15 2015 UTC (8 years, 10 months ago) by torben
File size: 36514 byte(s)
Added tpsystools component
1 torben 2671 // 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