📄 stdecmth.pas
字号:
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 + -