Ich habe eine symbolische Mathematik-Unit, die kann bis ein paar tausend Stellen, auch unter 32bit und 16bit Turbo-Pascal. Jedoch, es treibt einen der Sport:
ich wollte einen Tip von chatgpt umsetzen, der da lautete nimm doch ein Paar von Int32, also high und low.
Na gut, ich habe was gebastelt - das funktioniert aber leider mehr schlecht als recht, mindestens die div64, vermutlich aber auch sub64 und mul64 sind noch buggy, ich gehe mal davon aus mod64 ist dann als Folgefehler auch falsch,
Code: Alles auswählen
unit al32;
interface
type
TInt32P = record
LowPart, HighPart: LongInt;
end;
function MakeInt32Pair(High, Low: LongInt): TInt32P;
function Int32PToString(P: TInt32P): String;
function Int32PExtString(P: TInt32P): String;
function Add64(A, B: TInt32P): TInt32P;
function Sub64(A, B: TInt32P): TInt32P;
function Mul64(A, B: TInt32P): TInt32P;
function Div64(A, B: TInt32P): TInt32P;
function Mod64(A, B: TInt32P): TInt32P;
implementation
function MakeInt32Pair(High, Low: LongInt): TInt32P;
var
Pair: TInt32P;
begin
Pair.LowPart := Low;
Pair.HighPart := High;
MakeInt32Pair := Pair;
end;
function Int32PToString(P: TInt32P): String;
var
NumStr, ResultStr: String;
FullValue: Double;
Negative: Boolean;
i, Len, Count: Integer;
begin
Negative := P.HighPart < 0;
if Negative
then FullValue := -((not P.HighPart * 4294967296.0) - not P.LowPart + 1)
else FullValue := P.HighPart * 4294967296.0 + P.LowPart;
Str(Trunc(FullValue):0, NumStr);
ResultStr := '';
Len := Length(NumStr);
Count := 0;
for i := Len downto 1 do
begin
Inc(Count);
ResultStr := NumStr[i] + ResultStr;
if (Count mod 3 = 0) and (i > 1) then
ResultStr := '.' + ResultStr;
end;
if Negative then ResultStr := '-' + ResultStr;
Int32PToString := ResultStr;
end;
function Int32PExtString(P: TInt32P): String;
var
HighInt32, LowInt32: TInt32P;
HighStr, LowStr: String;
begin
HighInt32.HighPart := P.HighPart; HighInt32.LowPart := 0;
LowInt32.HighPart := 0; LowInt32.LowPart := P.LowPart;
HighStr := Int32PToString(HighInt32);
LowStr := Int32PToString(LowInt32);
Int32PExtString := '(' + HighStr + ' / ' + LowStr + ')';
end;
function Add64(A, B: TInt32P): TInt32P;
var
Sum: TInt32P;
Carry: LongInt;
begin
Sum.LowPart := A.LowPart + B.LowPart;
Carry := 0;
if (Sum.LowPart < A.LowPart) or (Sum.LowPart < B.LowPart) then
Carry := 1;
Sum.HighPart := A.HighPart + B.HighPart + Carry;
Add64 := Sum;
end;
function Sub64(A, B: TInt32P): TInt32P;
var
Difference: TInt32P;
Borrow: LongInt;
begin
Difference.LowPart := A.LowPart - B.LowPart;
Borrow := 0;
if (A.LowPart < B.LowPart) then
Borrow := 1;
Difference.HighPart := A.HighPart - B.HighPart - Borrow;
Sub64 := Difference;
end;
function Mul64(A, B: TInt32P): TInt32P;
var
Product: TInt32P;
AL, AH, BL, BH: LongInt;
begin
AL := A.LowPart; AH := A.HighPart;
BL := B.LowPart; BH := B.HighPart;
Product.LowPart := AL * BL;
Product.HighPart := AL * BH + AH * BL + (Product.LowPart shr 31);
Mul64 := Product;
end;
function Div64(A, B: TInt32P): TInt32P;
var
Dividend, Divisor, Quotient, TempDiv: TInt32P;
Shift: Integer;
begin
if (B.LowPart = 0) and (B.HighPart = 0) then begin
Writeln('Fehler: Division durch Null');
Halt(1);
end;
Dividend := A;
Divisor := B;
Quotient := MakeInt32Pair(0, 0);
TempDiv := Divisor;
Shift := 0;
while (TempDiv.HighPart >= 0) and (TempDiv.HighPart < $80000000) do begin
TempDiv.HighPart := (TempDiv.HighPart shl 1) or (TempDiv.LowPart shr 31);
TempDiv.LowPart := TempDiv.LowPart shl 1;
Inc(Shift);
end;
while Shift >= 0 do begin
if (Dividend.HighPart > TempDiv.HighPart) or
((Dividend.HighPart = TempDiv.HighPart) and (Dividend.LowPart >= TempDiv.LowPart)) then
begin
Dividend := Sub64(Dividend, TempDiv);
Quotient.LowPart := Quotient.LowPart or (1 shl Shift);
end;
TempDiv.LowPart := (TempDiv.LowPart shr 1) or ((TempDiv.HighPart and 1) shl 31);
TempDiv.HighPart := TempDiv.HighPart shr 1;
Dec(Shift);
end;
Div64 := Quotient;
end;
function Mod64(A, B: TInt32P): TInt32P;
var
Quotient, Product: TInt32P;
begin
Quotient := Div64(A, B);
Product := Mul64(Quotient, B);
Mod64 := Sub64(A, Product);
end;
begin
end.
Code: Alles auswählen
program al32prog;
uses al32;
var
A, B, R: TInt32P;
begin
A := MakeInt32Pair(1, 2147483647); { 2^32, 2^31-1 }
B := MakeInt32Pair(0, 2); { 2 }
Writeln('A: ' + Int32PToString(A), ' = ', Int32PExtString(A)); { 6.442.450.943 }
Writeln('B: ' + Int32PToString(B), ' = ', Int32PExtString(B)); { 2 }
R := Add64(A, B);
Writeln('A + B = ' + Int32PToString(R), ' = ', Int32PExtString(R)); { 6.442.450.945 }
R := Sub64(A, B);
Writeln('A - B = ' + Int32PToString(R), ' = ', Int32PExtString(R)); { 6.442.450.941 }
R := Mul64(A, B);
Writeln('A * B = ' + Int32PToString(R), ' = ', Int32PExtString(R)); { 12.884.901.886 }
R := Div64(A, B);
Writeln('A div B = ' + Int32PToString(R), ' = ', Int32PExtString(R)); { 3.221.225.471 }
R := Mod64(A, B);
Writeln('A mod B = ' + Int32PToString(R), ' = ', Int32PExtString(R)); { 1 }
Readln;
end.
Gibt es in Lazarus einen ähnlichen Datentypen und Beispieloperationen, idealerweise im Quelltext, so daß ich mal vergleichen kann?
Findet jemand Fehler in meinen Operationen?
Den Gedanken, einfach in Double zu rechnen (wie bei der ToString-Darstellung) hatte ich schon, aber da gibts bestimmt Rundungsprobleme und auch div wäre mir dann nicht geheuer. Ich würde es nur überlegen, wenn der high klein bleibt, so daß zB insgesamt 2^36 nicht überschritten wird, dann ist das wohl mit dem Timestamp noch abgedeckt.
Für Rechnungen würde ich wie gesagt die symbolische Mathematik nehmen - aber das mit dem Timestamp (2038 droht

Nebenbei: hat Lazarus auch für komplexe Zahlen/Arithmetik Units (wohl auch mit Paaren), die man mal im Quelltext anschauen könnte? Vielleicht bringt mich das ja auch Ideen