⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 stdecmth.pas

📁 条码控件: 一维条码控件 二维条码控件 PDF417Barcode MaxiCodeBarcode
💻 PAS
📖 第 1 页 / 共 3 页
字号:
    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 + -