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

📄 stdecmth.pas

📁 条码控件: 一维条码控件 二维条码控件 PDF417Barcode MaxiCodeBarcode
💻 PAS
📖 第 1 页 / 共 3 页
字号:
  mov eax, [ebp+8]
  mov ebx, [ebp+12]
  sbb eax, [edi+8]
  sbb ebx, [edi+12]
  mov [ebp+8], eax
  mov [ebp+12], ebx

@@TooSmall:
  dec ecx              // go get the next bit to work on
  jnz @@GetNextBit

  pop esi
  pop edi
  pop ebx
  pop ebp
end;
{--------}
function Int128DivInt(var X : TStInt128; aDivisor : integer) : integer;
{Note: this routine assumes X is positive}
asm
  push ebx            // save ebx

  mov ecx, edx        // ecx is now the divisor
  mov ebx, eax        // ebx points to X

  xor edx, edx        // start off with high dividend digit zero
  mov eax, [ebx+12]   // get last 32-bit digit
  div ecx             // divide by ecx: eax is quotient, edx remainder
  mov [ebx+12], eax   // save highest quotient digit

  mov eax, [ebx+8]    // get next 32-bit digit
  div ecx             // divide by ecx: eax is quotient, edx remainder
  mov [ebx+8], eax    // save next quotient digit

  mov eax, [ebx+4]    // get next 32-bit digit
  div ecx             // divide by ecx: eax is quotient, edx remainder
  mov [ebx+4], eax    // save next quotient digit

  mov eax, [ebx]      // get first 32-bit digit
  div ecx             // divide by ecx: eax is quotient, edx remainder
  mov [ebx], eax      // save first quotient digit

  mov eax, edx        // return remainder

  pop ebx             // restore ebx
end;
{--------}
procedure Int128Divide(var X, Y : TStInt128);
var
  XTemp : TStInt192;
  Rem   : TStInt128;
begin
  {note: the easy cases have been dealt with
         X and Y are both positive
         X will be set to the quotient X/Y and Y will be trashed}

  {we need to increase the number of decimal places to 32, so convert
   the 128 bit dividend to a 192 bit one and multiply by 10^16}
  XTemp[0] := X[0];
  XTemp[1] := X[1];
  XTemp[2] := X[2];
  XTemp[3] := X[3];
  XTemp[4] := 0;
  XTemp[5] := 0;
  Int192Times10E8(XTemp);
  Int192Times10E8(XTemp);

  {Note: this algorithm follows that described by Knuth in volume 2 of
         The Art of Computer Programming. Algorithm D of section 4.3
         as applied to binary numbers (radix=2)}

  {divide the 192-bit dividend by the 128-bit divisor}
  Int128FastDivide(XTemp, Y, Rem);

  {have we overflowed? ie, have we divided a very big number by one
   much less than zero}
  if (XTemp[3] < 0) or (XTemp[4] <> 0) or (XTemp[5] <> 0) then
    raise EStDecMathError.Create(stscDecMathDivOverflowS);

  {return the result of the computation}
  X[0] := XTemp[0];
  X[1] := XTemp[1];
  X[2] := XTemp[2];
  X[3] := XTemp[3];
end;
{--------}
procedure Int128Multiply(var X, Y : TStInt128);
var
  P : TStInt256;
  XIsNeg : boolean;
  YIsNeg : boolean;
  YInx   : integer;
  YDigit : integer;
  Carry  : integer;
  YTemp  : TStInt128;
begin
  {Note: calculates X * Y and puts the answer in X}

  {get rid of the easy cases where one of the operands is zero}
  if (X[0] = 0) and (X[1] = 0) and (X[2] = 0) and (X[3] = 0) then
    Exit;
  if (Y[0] = 0) and (Y[1] = 0) and (Y[2] = 0) and (Y[3] = 0) then begin
    X[0] := 0;
    X[1] := 0;
    X[2] := 0;
    X[3] := 0;
    Exit;
  end;

  {we might need to trash Y, so we use a local variable}
  YTemp[0] := Y[0];
  YTemp[1] := Y[1];
  YTemp[2] := Y[2];
  YTemp[3] := Y[3];

  {convert both operands to positive values: we'll fix the sign later}
  XIsNeg := X[3] < 0;
  if XIsNeg then
    Int128ChgSign(X);
  YIsNeg := YTemp[3] < 0;
  if YIsNeg then
    Int128ChgSign(YTemp);

  {initialize the temporary product}
  P[0] := 0;
  P[1] := 0;
  P[2] := 0;
  P[3] := 0;
  P[4] := 0;
  P[5] := 0;
  P[6] := 0;
  P[7] := 0;

  {for every digit in Y we shall multiply by all the X digits and sum}
  for YInx := 0 to 3 do begin

    {get the Y digit}
    YDigit := YTemp[YInx];

    {there's only something to do if the Y digit is non-zero}
    if (YDigit <> 0) then begin

      {multiply this digit with all the X digits, storing the result
       in the temporary product}
      Carry := Int32MultPrim(X[0], YDigit, P[YInx], 0);
      Carry := Int32MultPrim(X[1], YDigit, P[YInx + 1], Carry);
      Carry := Int32MultPrim(X[2], YDigit, P[YInx + 2], Carry);
      P[YInx + 4] := Int32MultPrim(X[3], YDigit, P[YInx + 3], Carry);
    end;
  end;

  {the product has 32 decimal places, so divide by 10^8 twice to get
   the answer to the 16 decimal places we need}
  Int256Div10E8(P, Carry);
  Int256Div10E8(P, Carry);

  {note: if Carry <> 0 then we're losing precision}

  {check for multiplication overflow}
  if (P[3] < 0) or
     (P[4] <> 0) or (P[5] <> 0) or (P[6] <> 0) or (P[7] <> 0) then
    raise EStDecMathError.Create(stscDecMathMultOverflowS);

  {return the value in X, remembering to set the sign}
  X[0] := P[0];
  X[1] := P[1];
  X[2] := P[2];
  X[3] := P[3];

  (*
  {round if necessary}
  if (Carry >= 500000000) then
    Int128AddInt(X, 1);
  *)

  {set the sign}
  if (XIsNeg xor YIsNeg) then
    Int128ChgSign(X);
end;
{--------}
procedure Int128TimesInt(var X : TStInt128; aValue : integer);
{Note: this routine assumes X is positive}
asm
  push ebx             // save ebx
  push ebp             // save ebp

  mov ecx, edx         // we're multiplying by aValue
  mov ebx, eax         // ebx points to X

  mov eax, [ebx]       // get the first 32-bit digit
  mul ecx              // multiply it by 10 to give answer in edx:eax
  mov [ebx], eax       // save first digit of result
  mov ebp, edx         // save overflow

  mov eax, [ebx+4]     // get the second 32-bit digit
  mul ecx              // multiply it by 10 to give answer in edx:eax
  add eax, ebp         // add the overflow from the first digit
  adc edx, 0
  mov [ebx+4], eax     // save second digit of result
  mov ebp, edx         // save overflow

  mov eax, [ebx+8]     // get the third 32-bit digit
  mul ecx              // multiply it by 10 to give answer in edx:eax
  add eax, ebp         // add the overflow from the second digit
  adc edx, 0
  mov [ebx+8], eax     // save second digit of result
  mov ebp, edx         // save overflow

  mov eax, [ebx+12]     // get the third 32-bit digit
  mul ecx              // multiply it by 10 to give answer in edx:eax
  add eax, ebp         // add the overflow from the second digit
  mov [ebx+12], eax     // save third digit of result

  pop ebp              // restore ebp
  pop ebx              // restore ebx
end;
{--------}
procedure Int128Round(var X : TStInt128;
                          aRound : TStRoundMethod;
                          aDecPl : integer);
var
  Rem     : integer;
  HadRem  : boolean;
  AddOne  : boolean;
  Expnt   : integer;
  NeedInt : boolean;
begin
  {Assumptions: X is positive, 0 <= aDecPl <= MaxDecPl
                --the caller *must* ensure these}

  {if the number of decimal places is -1, it's a special signal to
   perform the rounding to an integer, but not to multiply the result
   by 10^16 at the end; the caller is AsInt, in other words}
  if (aDecPl >= 0) then
    NeedInt := false
  else begin
    NeedInt := true;
    aDecPl := 0;
  end;

  {if we're asked to round to the precision of the type, there's
   nothing to do}
  if (aDecPl = MaxDecPl) then
    Exit;

  {perform the required rounding}
  AddOne := false;   // keep the compiler happy
  case aRound of
    rmNormal :
      begin
        {to do normal rounding: divide by the required power of ten,
         if the most significant digit of the remainder was 5 or more,
         we'll add one to the result}
        Expnt := MaxDecPl - aDecPl - 1;
        if (Expnt > 0) then begin
          if (Expnt > 8) then begin
            Int128DivInt(X, PowerOf10[8]);
            dec(Expnt, 8);
          end;
          Int128DivInt(X, PowerOf10[Expnt]);
        end;
        AddOne := Int128DivInt(X, 10) >= 5;
      end;
    rmTrunc :
      begin
        {to truncate: just divide by the required power of ten}
        Expnt := MaxDecPl - aDecPl;
        if (Expnt > 8) then begin
          Int128DivInt(X, PowerOf10[8]);
          dec(Expnt, 8);
        end;
        Int128DivInt(X, PowerOf10[Expnt]);
        AddOne := false;
      end;
    rmBankers :
      begin
        {to do bankers rounding:
          - divide by the required power of ten, checking to see if
            there's a non-zero remainder
          - if the most significant digit of the remainder was greater
            than 5, we'll add one to the result
          - if the most significant digit of the remainder was 5 and
            there was at least one other digit in the remainder, we'll
            add one to the result
          - if the most significant digit of the remainder was 5 and
            there were no other digits in the remainder, determine if
            the result is odd; if it is, we'll add one to the result}
        HadRem := false;
        if ((MaxDecPl - aDecPl) > 1) then begin
          Expnt := MaxDecPl - aDecPl - 1;
          if (Expnt > 8) then begin
            if (Int128DivInt(X, PowerOf10[8]) <> 0) then
              HadRem := true;
            dec(Expnt, 8);
          end;
          if (Int128DivInt(X, PowerOf10[Expnt]) <> 0) then
            HadRem := true;
        end;
        Rem := Int128DivInt(X, 10);
        AddOne := (Rem > 5) or
                  ((Rem = 5) and HadRem) or
                  ((Rem = 5) and Odd(X[0]));
      end;
    rmUp :
      begin
        {to always round up: divide by the required power of ten,
         if there was a remainder, we'll add one to the result}
        AddOne := false;
        Expnt := MaxDecPl - aDecPl;
        if (Expnt > 8) then begin
          if (Int128DivInt(X, PowerOf10[8]) <> 0) then
            AddOne := true;
          dec(Expnt, 8);
        end;
        if (Int128DivInt(X, PowerOf10[Expnt]) <> 0) then
          AddOne := true;
      end;
  end;{case}

  {add one to the result, if required}
  if AddOne then
    Int128AddInt(X, 1);

  {finally, multiply by the required power of ten}
  if not NeedInt then begin
    Expnt := MaxDecPl - aDecPl;
    if (Expnt > 8) then begin
      Int128TimesInt(X, PowerOf10[8]);
      dec(Expnt, 8);
    end;
    Int128TimesInt(X, PowerOf10[Expnt]);
  end;
end;
{====================================================================}


{====================================================================}
constructor TStDecimal.Create;
begin
  {create the ancestor}
  inherited Create;
  {note: the internal number will be automatically zero}
end;
{--------}
destructor TStDecimal.Destroy;
begin
  {free the ancestor}
  inherited Destroy;
end;
{--------}
procedure TStDecimal.Abs;
begin
  if (FInt[3] < 0) then
    Int128ChgSign(FInt);
end;
{--------}
procedure TStDecimal.Add(X : TStDecimal);
begin
  if (X <> nil) then
    Int128Add(FInt, X.FInt);
end;
{--------}
procedure TStDecimal.AddOne;
var
  One : TStInt128;
begin
  One[0] := Int128One_0;
  One[1] := Int128One_1;
  One[2] := 0;
  One[3] := 0;
  Int128Add(FInt, One);
end;
{--------}
function TStDecimal.AsFloat : double;
begin
  Result := StrToFloat(AsString);
end;
{--------}
function TStDecimal.AsInt(aRound : TStRoundMethod) : integer;
var
  X : TStInt128;
  IsNeg : boolean;
begin
  {get the current value locally}
  X[0] := FInt[0];
  X[1] := FInt[1];
  X[2] := FInt[2];
  X[3] := FInt[3];

  {force it to be positive}
  IsNeg := X[3] < 0;
  if IsNeg then
    Int128ChgSign(X);

  {round it to an integer}
  Int128Round(X, aRound, -1);

  {check for errors (the least significant digit cannot be negative,
   and all the others must be zero)}
  if (X[0] < 0) or (X[1] <> 0) or (X[2] <> 0) or (X[3] <> 0) then
    raise EStDecMathError.Create(stscDecMathAsIntOverflowS);

  {return the result}
  if IsNeg then
    Result := -X[0]
  else
    Result := X[0];
end;
{--------}
procedure TStDecimal.Assign(X : TStDecimal);
begin
  if (X = nil) then
    SetToZero
  else begin
    FInt[0] := X.FInt[0];
    FInt[1] := X.FInt[1];
    FInt[2] := X.FInt[2];
    FInt[3] := X.FInt[3];
  end;
end;
{--------}
procedure TStDecimal.AssignFromFloat(aValue : double);
begin
  AsString := Format('%38.16f', [aValue]);
end;
{--------}
procedure TStDecimal.AssignFromInt(aValue : integer);
begin
  FInt[0] := System.Abs(aValue);
  FInt[1] := 0;
  FInt[2] := 0;
  FInt[3] := 0;
  Int128TimesInt(FInt, PowerOf10[8]);
  Int128TimesInt(FInt, PowerOf10[8]);
  if (aValue < 0) then

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -