📄 stdecmth.pas
字号:
Int128ChgSign(FInt);
end;
{--------}
procedure TStDecimal.ChangeSign;
begin
Int128ChgSign(FInt);
end;
{--------}
function TStDecimal.Compare(X : TStDecimal) : integer;
begin
Compare := Int128Compare(FInt, X.FInt);
end;
{--------}
function TStDecimal.dcGetAsStr : AnsiString;
var
X : TStInt128;
i : integer;
Rem : integer;
IsNeg : boolean;
ChStack: array [0..47] of AnsiChar;
// this is ample for 38 digits + punctuation
ChSP : integer;
begin
{initialize the stack}
ChSP := 0;
{since we're going to trash the value, store it locally}
X[0] := FInt[0];
X[1] := FInt[1];
X[2] := FInt[2];
X[3] := FInt[3];
{make sure it's positive}
IsNeg := X[3] < 0;
if IsNeg then
Int128ChgSign(X);
{push the least significant digits (those that will appear after the
radix point)}
for i := 1 to MaxDecPl do begin
Rem := Int128DivInt(X, 10);
ChStack[ChSP] := AnsiChar(Rem + ord('0'));
inc(ChSP);
end;
{push the radix point}
ChStack[ChSP] := DecimalSeparator;
inc(ChSP);
{repeat until the local value is zero}
repeat
Rem := Int128DivInt(X, 10);
ChStack[ChSP] := AnsiChar(Rem + ord('0'));
inc(ChSP);
until (X[0] = 0) and (X[1] = 0) and (X[2] = 0) and (X[3] = 0);
{if the value was negative, push a minus sign}
if IsNeg then begin
ChStack[ChSP] := '-';
inc(ChSP);
end;
{construct the result value by popping off characters}
SetLength(Result, ChSP);
i := 1;
while (ChSP <> 0) do begin
dec(ChSP);
Result[i] := ChStack[ChSP];
inc(i);
end;
end;
{--------}
procedure TStDecimal.dcSetFromStr(const aValue : AnsiString); {!!.02}
var
State : (ScanStart, ScanSign, ScanRadix, ScanBefore,
ScanAfter, ScanEnd, GotError);
i : integer;
Ch : AnsiChar;
IsNeg : boolean;
DecPlCount : integer;
begin
{Note: this implements the following DFA:
ScanStart --space--> ScanStart
ScanStart --plus---> ScanSign
ScanStart --minus--> ScanSign
ScanStart --digit--> ScanBefore
ScanStart --radix--> ScanRadix
ScanSign --radix--> ScanRadix
ScanSign --digit--> ScanBefore
ScanRadix --digit--> ScanAfter
ScanBefore --radix--> ScanAfter
ScanBefore --digit--> ScanBefore
ScanBefore --space--> ScanEnd
ScanAfter --digit--> ScanAfter
ScanAfter --space--> ScanEnd
ScanEnd --space--> ScanEnd
The terminating states are ScanBefore, ScanAfter and ScanEnd; in
other words, a valid numeric string cannot end in a radix point.
}
{initialize}
SetToZero;
DecPlCount := 0;
IsNeg := false;
State := ScanStart;
{read through the input string}
for i := 1 to length(aValue) do begin
{get the current character}
Ch := aValue[i];
case State of
ScanStart :
begin
if ('0' <= Ch) and (Ch <= '9') then begin
FInt[0] := ord(Ch) - ord('0');
State := ScanBefore;
end
else if (Ch = '+') then begin
State := ScanSign;
end
else if (Ch = '-') then begin
IsNeg := true;
State := ScanSign;
end
else if (Ch = DecimalSeparator) then begin
State := ScanRadix;
end
else if (Ch <> ' ') then
State := GotError;
end;
ScanSign :
begin
if ('0' <= Ch) and (Ch <= '9') then begin
FInt[0] := ord(Ch) - ord('0');
State := ScanBefore;
end
else if (Ch = DecimalSeparator) then begin
State := ScanRadix;
end
else
State := GotError;
end;
ScanRadix :
begin
if ('0' <= Ch) and (Ch <= '9') then begin
inc(DecPlCount);
Int128TimesInt(FInt, 10);
Int128AddInt(FInt, ord(Ch) - ord('0'));
State := ScanAfter;
end
else
State := GotError;
end;
ScanBefore :
begin
if ('0' <= Ch) and (Ch <= '9') then begin
Int128TimesInt(FInt, 10);
Int128AddInt(FInt, ord(Ch) - ord('0'));
end
else if (Ch = DecimalSeparator) then begin
State := ScanAfter;
end
else if (Ch = ' ') then
State := ScanEnd
else
State := GotError;
end;
ScanAfter :
begin
if ('0' <= Ch) and (Ch <= '9') then begin
inc(DecPlCount);
if (DecPlCount <= MaxDecPl) then begin
Int128TimesInt(FInt, 10);
Int128AddInt(FInt, ord(Ch) - ord('0'));
end;
end
else if (Ch = ' ') then
State := ScanEnd
else
State := GotError;
end;
ScanEnd :
begin
if (Ch <> ' ') then
State := GotError;
end;
GotError :
begin
Break;
end;
end;
end;
if (State <> ScanBefore) and
(State <> ScanAfter) and
(State <> ScanEnd) then
raise EStDecMathError.Create(stscDecMathConversionS);
{make sure we have the correct number of decimal places}
if (MaxDecPl > DecPlCount) then begin
DecPlCount := MaxDecPl - DecPlCount;
if (DecPlCount > 8) then begin
Int128TimesInt(FInt, Powerof10[8]);
dec(DecPlCount, 8);
end;
Int128TimesInt(FInt, Powerof10[DecPlCount]);
end;
{force negative, if required}
if IsNeg then
Int128ChgSign(FInt);
end;
{--------}
procedure TStDecimal.Divide(X : TStDecimal);
var
TempX : TStInt128;
IsNeg : boolean;
XIsNeg : boolean;
begin
{easy case: X is nil or zero}
if (X = nil) or X.IsZero then
raise EStDecMathError.Create(stscDecMathDivByZeroS);
{easy case: Self is zero}
if IsZero then
Exit;
{we might have to change X, so make it local}
TempX[0] := X.FInt[0];
TempX[1] := X.FInt[1];
TempX[2] := X.FInt[2];
TempX[3] := X.FInt[3];
{force the divisor and dividend positive}
IsNeg := FInt[3] < 0;
if IsNeg then
Int128ChgSign(FInt);
XIsNeg := TempX[3] < 0;
if XIsNeg then
Int128ChgSign(TempX);
{easy case: X is 1.0: set the correct sign}
if (TempX[0] = Int128One_0) and (TempX[1] = Int128One_1) and
(TempX[2] = 0) and (TempX[3] = 0) then begin
if (IsNeg xor XIsNeg) then
Int128ChgSign(FInt);
Exit;
end;
{easy case: compare the dividend and divisor: if they're equal,
set ourselves to 1.0 with the correct sign}
if (Int128Compare(FInt, TempX) = 0) then begin
FInt[0] := Int128One_0;
FInt[1] := Int128One_1;
FInt[2] := 0;
FInt[3] := 0;
if (IsNeg xor XIsNeg) then
Int128ChgSign(FInt);
Exit;
end;
{no more easy cases: just do the division}
Int128Divide(FInt, TempX);
{set the sign}
if (IsNeg xor XIsNeg) then
Int128ChgSign(FInt);
end;
{--------}
function TStDecimal.IsNegative : boolean;
begin
{if the most significant longint is negative, so is the value}
Result := FInt[3] < 0;
end;
{--------}
function TStDecimal.IsOne : boolean;
begin
Result := (FInt[0] = Int128One_0) and (FInt[1] = Int128One_1) and
(FInt[2] = 0) and (FInt[3] = 0);
end;
{--------}
function TStDecimal.IsPositive : boolean;
begin
{if the most significant longint is positive, so is the value; if it
is zero, one of the other longints must be non-zero for the value
to be positive}
Result := (FInt[3] > 0) or
((FInt[3] = 0) and
((FInt[2] <> 0) or (FInt[1] <> 0) or (FInt[0] <> 0)));
end;
{--------}
function TStDecimal.IsZero : boolean;
begin
Result := (FInt[0] = 0) and (FInt[1] = 0) and
(FInt[2] = 0) and (FInt[3] = 0);
end;
{--------}
procedure TStDecimal.Multiply(X : TStDecimal);
begin
if (X = nil) then
SetToZero
else
Int128Multiply(FInt, X.FInt);
end;
{--------}
procedure TStDecimal.RaiseToPower(N : integer);
var
Accum : TStInt128;
Mask : longint;
IsNeg : boolean;
begin
{take care of some easy cases}
if (N < 0) then
raise EStDecMathError.Create(stscDecMathNegExpS);
if (N = 0) then begin
SetToOne;
Exit;
end;
if (N = 1) then
Exit;
{force the value positive}
IsNeg := FInt[3] < 0;
if IsNeg then
Int128ChgSign(FInt);
{initialize the accumulator to 1.0}
Accum[0] := Int128One_0;
Accum[1] := Int128One_1;
Accum[2] := 0;
Accum[3] := 0;
{set the bit mask}
Mask := longint($80000000);
{find the first set bit in the exponent}
while ((N and Mask) = 0) do
Mask := Mask shr 1;
{calculate the power}
while (Mask <> 0) do begin
Int128Multiply(Accum, Accum);
if ((N and Mask) <> 0) then
Int128Multiply(Accum, FInt);
Mask := Mask shr 1;
end;
{save the calculated value}
FInt[0] := Accum[0];
FInt[1] := Accum[1];
FInt[2] := Accum[2];
FInt[3] := Accum[3];
{force the value negative if required}
if IsNeg and Odd(N) then
Int128ChgSign(FInt);
end;
{--------}
procedure TStDecimal.Round(aRound : TStRoundMethod; aDecPl : integer);
var
IsNeg : boolean;
begin
{check decimal places parameter to be in range}
if not ((0 <= aDecPl) and (aDecPl <= MaxDecPl)) then
raise EStDecMathError.Create(stscDecMathRoundPlacesS);
{force the value positive}
IsNeg := FInt[3] < 0;
if IsNeg then
Int128ChgSign(FInt);
{perform the rounding}
Int128Round(FInt, aRound, aDecPl);
{force the value negative if it was negative}
if IsNeg then
Int128ChgSign(FInt);
end;
{--------}
procedure TStDecimal.SetToOne;
begin
FInt[0] := Int128One_0;
FInt[1] := Int128One_1;
FInt[2] := 0;
FInt[3] := 0;
end;
{--------}
procedure TStDecimal.SetToZero;
begin
FInt[0] := 0;
FInt[1] := 0;
FInt[2] := 0;
FInt[3] := 0;
end;
{--------}
procedure TStDecimal.Subtract(X : TStDecimal);
var
MinusX : TStInt128;
begin
if (X <> nil) then begin
MinusX[0] := X.FInt[0];
MinusX[1] := X.FInt[1];
MinusX[2] := X.FInt[2];
MinusX[3] := X.FInt[3];
Int128ChgSign(MinusX);
Int128Add(Fint, MinusX);
end;
end;
{--------}
procedure TStDecimal.SubtractOne;
var
MinusOne : TStInt128;
begin
MinusOne[0] := Int128One_0;
MinusOne[1] := Int128One_1;
MinusOne[2] := 0;
MinusOne[3] := 0;
Int128ChgSign(MinusOne);
Int128Add(FInt, MinusOne);
end;
{====================================================================}
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -