Wie rechne ich richtig mit TInt32Pair?

Für Fragen von Einsteigern und Programmieranfängern...
Antworten
alfware17
Beiträge: 210
Registriert: Di 14. Dez 2010, 23:27

Wie rechne ich richtig mit TInt32Pair?

Beitrag von alfware17 »

Es ist nur ein Nebenproblem - ich habe manchmal kein Int64 vorhanden obwohl ich es gerne brauchte zB für den Unit-Timestamp oder wenn mir Rechnungen arg groß werden.
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.
und folgendes Aufrufprogramm

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.
Ja, ich habe auch schon mit nur low gefüllt getestet, also einfache Integer-Arithmetik und das läuft auch schon schief, also ist was faul. Aber was?

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 :-) würde ich schon mal als Problem sehen.
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

wp_xyz
Beiträge: 5129
Registriert: Fr 8. Apr 2011, 09:01

Re: Wie rechne ich richtig mit TInt32Pair?

Beitrag von wp_xyz »

alfware17 hat geschrieben: Sa 22. Mär 2025, 14:44 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
ucomplex

Benutzeravatar
corpsman
Lazarusforum e. V.
Beiträge: 1617
Registriert: Sa 28. Feb 2009, 08:54
OS, Lazarus, FPC: Linux Mint Mate, Lazarus GIT Head, FPC 3.0
CPU-Target: 64Bit
Wohnort: Stuttgart
Kontaktdaten:

Re: Wie rechne ich richtig mit TInt32Pair?

Beitrag von corpsman »

Ich hab da auch mal was dazu gemacht, das example findest du hier: https://github.com/PascalCorpsman/Examp ... ol/Complex
--
Just try it

Benutzeravatar
Zvoni
Beiträge: 363
Registriert: Fr 5. Jul 2024, 08:26
OS, Lazarus, FPC: Windoof 10 Pro (Laz 2.2.2 FPC 3.2.2)
CPU-Target: 32Bit
Wohnort: BW

Re: Wie rechne ich richtig mit TInt32Pair?

Beitrag von Zvoni »

Doofe Frage: Hast du schon gecheckt ob "Comp" oder "Currency" zur Verfügung stehen?

https://www.freepascal.org/docs-html/cu ... efsu5.html
Ein System sie alle zu knechten, ein Code sie alle zu finden,
Eine IDE sie ins Dunkel zu treiben, und an das Framework ewig zu binden,
Im Lande Redmond, wo die Windows drohn.

PascalDragon
Beiträge: 945
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: Wie rechne ich richtig mit TInt32Pair?

Beitrag von PascalDragon »

alfware17 hat geschrieben: Sa 22. Mär 2025, 14:44 Gibt es in Lazarus einen ähnlichen Datentypen und Beispieloperationen, idealerweise im Quelltext, so daß ich mal vergleichen kann?
FPC hat Funktionen für Systeme, die 64-bit Typen nicht nativ beherrschen. Siehe hier.
FPC Compiler Entwickler

alfware17
Beiträge: 210
Registriert: Di 14. Dez 2010, 23:27

Re: Wie rechne ich richtig mit TInt32Pair?

Beitrag von alfware17 »

Zunächst mal danke für eure Hinweise und die Beispiele zum complex!

Was das TInt32Pair angeht, so muß ich glaube ich zugeben, daß ich da einem Phantom hinterherrenne. Nachdem ich Add64, Sub64 und Mod64 (wenn auch ungetestet, da Mul64 und Div64 noch fehlen) relativ gut hinbekommen habe, kam mir dann bei MUL64 nach der 12.Version von chatgpt und sogar Formeln, Schritten, Erklärungen beim Debuggen die Erleuchtung: Das geht ja gar nicht, weil es aus dem Zahlenbereich herausführt.
Beispiel (noch einfach): A(7, 0) * B (3, 0) ist natürlich nicht! (21, 0) sondern es wäre (21 * 2^31, 0) und selbst diese einfache Rechnung geht schon aus dem Bereich des oberen LongInt heraus. Da kann ich mir die ganze Übertragsrechnung vom unteren LongInt zum oberen sparen. Sprich es ist mein Denkfehler. Man kann vielleicht 32bit auf 16bit emulieren respektive 64bit auf 32bit emulieren - das Ergebnis sprengt dann allerdings die Ketten und ist nur in der doppelten Speichermenge darstellbar.
Ich werde jetzt vielleicht noch eine MUL und DIV light machen, wo der zweite Operant nur LongInt ist.

Das Beispiel von PascalDragon habe ich mir angeschaut, aber ich fürchte Turbo Pascal hat kein dword und kein qword oder?

@ Zvoni: nein ich fürchte das gibt es in Turbo-Pascal auch nicht. In Free Pascal kann ich ja Int64 nehmen und so wie ich es verstehe, in FreePascal 64 ist es native und in Free Pascal 32 eben emuliert aber schon durch die Sprache, den Compiler, die Maschine unterstützt. Was ich machen wollte, war das in Turbo auch nachbilden.

Benutzeravatar
Zvoni
Beiträge: 363
Registriert: Fr 5. Jul 2024, 08:26
OS, Lazarus, FPC: Windoof 10 Pro (Laz 2.2.2 FPC 3.2.2)
CPU-Target: 32Bit
Wohnort: BW

Re: Wie rechne ich richtig mit TInt32Pair?

Beitrag von Zvoni »

alfware17 hat geschrieben: Di 25. Mär 2025, 10:21 @ Zvoni: nein ich fürchte das gibt es in Turbo-Pascal auch nicht. In Free Pascal kann ich ja Int64 nehmen und so wie ich es verstehe, in FreePascal 64 ist es native und in Free Pascal 32 eben emuliert aber schon durch die Sprache, den Compiler, die Maschine unterstützt. Was ich machen wollte, war das in Turbo auch nachbilden.
Errrr.....???? --> https://turbopascal.org/system-type-definitions
Scrolle ich ein Drittel nach unten sehe ich deutlich

Code: Alles auswählen

With CompTypeDefinition do
        begin
          BaseType             := btExtended;
          DataType             := fpComp;  //<--- DER HIER
          Size                 := 8;
          TypeIdentifierOffset := Comp_TypeNameOffset;
          W06_     := $0015;
        end;
--> https://d3s.mff.cuni.cz/legacy/teaching ... 00615.html
Note: The comp type is a 64-bit integer. It holds only integral values
within the range (-2 63 + 1) to (2 63 - 1).
Der Punkt worauf ich hinaus will: Wenn es den "Comp" tatsächlich gibt, wieso brichst du dir dann so nen Wolf mit dem Int32Pair?

EDIT: Also egal wo ich geschaut habe, überall wird der "Comp" erwähnt....
--> https://www.gnu-pascal.de/gpc/Data-Type ... d-GPC.html

Generell zum Int32Pair: Was ich eher seltsam finde ist, dass der LowPart Vorzeichenbehaftet ist.
Hätte "ein" Vorzeichen "nur" beim Highpart erwartet, da du ja eigentlich nur die 2x32 Bits willst (bzw. 63 Bits für den Wert. Das höchste Bit is ja das Vorzeichen)
Zuletzt geändert von Zvoni am Di 25. Mär 2025, 14:07, insgesamt 1-mal geändert.
Ein System sie alle zu knechten, ein Code sie alle zu finden,
Eine IDE sie ins Dunkel zu treiben, und an das Framework ewig zu binden,
Im Lande Redmond, wo die Windows drohn.

alfware17
Beiträge: 210
Registriert: Di 14. Dez 2010, 23:27

Re: Wie rechne ich richtig mit TInt32Pair?

Beitrag von alfware17 »

Zvoni hat geschrieben: Di 25. Mär 2025, 10:34
alfware17 hat geschrieben: Di 25. Mär 2025, 10:21 @ Zvoni: nein ich fürchte das gibt es in Turbo-Pascal auch nicht. In Free Pascal kann ich ja Int64 nehmen und so wie ich es verstehe, in FreePascal 64 ist es native und in Free Pascal 32 eben emuliert aber schon durch die Sprache, den Compiler, die Maschine unterstützt. Was ich machen wollte, war das in Turbo auch nachbilden.
Errrr.....???? --> https://turbopascal.org/system-type-definitions
Scrolle ich ein Drittel nach unten sehe ich deutlich

Code: Alles auswählen

With CompTypeDefinition do
        begin
          BaseType             := btExtended;
          DataType             := fpComp;  //<--- DER HIER
          Size                 := 8;
          TypeIdentifierOffset := Comp_TypeNameOffset;
          W06_     := $0015;
        end;
--> https://d3s.mff.cuni.cz/legacy/teaching ... 00615.html
Note: The comp type is a 64-bit integer. It holds only integral values
within the range (-2 63 + 1) to (2 63 - 1).
Der Punkt worauf ich hinaus will: Wenn es den "Comp" tatsächlich gibt, wieso brichst du dir dann so nen Wolf mit dem Int32Pair?

EDIT: Also egal wo ich geschaut habe, überall wird der "Comp" erwähnt....
--> https://www.gnu-pascal.de/gpc/Data-Type ... d-GPC.html

Generell zum Int32Pair: Was ich eher seltsam finde ist, dass der LowPart Vorzeichenbehaftet ist.
Hätte "ein" Vorzeichen "nur" beim Highpart erwartet, da du ja eigentlich nur die 2x32 Bits willst
Gute Frage... weil ich es bisher nicht kannte oder nicht wahrgenommen, verdrängt oder schlicht vergessen habe.
Allerdings verhält sich COMP mehr wie ein Gleitkomma (kein INC, DEC und DIV) aber hat soweit ich sehen kann alle 19 gültigen (Decimal) Stellen für 64 Bit und folgt damit
in der Mantisse dem Extended (nur daß der Exponent eben nur +/-63 erlaubt ist). Wenn ich jetzt noch herausfinde, daß man damit vernünftig schnell rechnen kann,
wäre es für Turbo eine echte Alternative. Bisher heißt mein Datentyp CARD entweder Int64 oder Longint - aber ich weiß wohl schon vorher, komplett ersetzen kann ich es nicht,
eben weil INC, DEC, DIV und vermutlich nicht einmal MOD (nicht) gehen wird. Aber ok zum Rechnen...
Nebenbei, das mit der Mantisse habe ich erst spät gesehen - meine StrDouble() und StrCard machen leider genau das, was sie heißen. Die Genauigkeit ist schon da
nur geht sie beim Aufruf meiner Prozedur durch Konvertierung dann flöten. Ich werde mir noch ein StrExtended() und StrComp() schreiben müssen, brauchte ich nur bisher nicht.

Code: Alles auswählen

{$i oswahl.inc}
{$ifndef dos} {$N+}{$d+} {$else} {$mode objfpc} {$endif}
program test;
uses stdio,sysutils;
var c: comp;
    d: double;
    e: extended;
    l: longint;
    {$ifndef dos} x: int64; {$endif}
    i: integer;
begin
   c:=1;
   d:=1;
   e:=1;
   l:=1;
   {$ifndef dos} x:=1; {$endif}
   for i:=1 to 64 do begin
      c:=c*2;
      d:=d*2;
      e:=e*2;
      l:=l*2;
      {$ifndef dos} x:=x*2; {$endif}
      writeln(i,' ',c,' ',
                    d,' ',
                    e,' '
                    {$ifndef dos},' ',x{$endif}
                    );
      writeln(i,' ',strdouble(c),' ',
                    strdouble(d),' ',
                    strdouble(e),' ',
                    strcard(l)
                    {$ifndef dos},' ',strcard(x){$endif}
                    );
   end;
end.

alfware17
Beiträge: 210
Registriert: Di 14. Dez 2010, 23:27

Re: Wie rechne ich richtig mit TInt32Pair?

Beitrag von alfware17 »

Zu der Frage mit dem Seltsam-Vorzeichen von HighPart und LowPart von TInt32Pair. Nunja, so ganz klar ist mir das nicht, wohin das Vorzeichen gehört und was wäre wenn die sich widersprechen. Es ist nur so, als ich den Typ entworfen habe, hatte ich leider nur LongInt zur Verfügung, kein LongWord. Damit ist LowPart auch kleiner als eigentlich angenommen (von ChatGPT der mich mit Algorithmen zum Verschieben und Carry verwirrt hat). Ich glaube, generell ist da der Wurm drin. Wenn COMP die Grundrechenarten als Integer besteht, kann ich den nehmen.

Benutzeravatar
Zvoni
Beiträge: 363
Registriert: Fr 5. Jul 2024, 08:26
OS, Lazarus, FPC: Windoof 10 Pro (Laz 2.2.2 FPC 3.2.2)
CPU-Target: 32Bit
Wohnort: BW

Re: Wie rechne ich richtig mit TInt32Pair?

Beitrag von Zvoni »

alfware17 hat geschrieben: Di 25. Mär 2025, 13:53 Zu der Frage mit dem Seltsam-Vorzeichen von HighPart und LowPart von TInt32Pair. Nunja, so ganz klar ist mir das nicht, wohin das Vorzeichen gehört und was wäre wenn die sich widersprechen. Es ist nur so, als ich den Typ entworfen habe, hatte ich leider nur LongInt zur Verfügung, kein LongWord. Damit ist LowPart auch kleiner als eigentlich angenommen (von ChatGPT der mich mit Algorithmen zum Verschieben und Carry verwirrt hat). Ich glaube, generell ist da der Wurm drin. Wenn COMP die Grundrechenarten als Integer besteht, kann ich den nehmen.
Ist eigentlich "logisch" wenn man darüber nachdenkt: Du brauchst ja nur "ein" Vorzeichen, und das ist in der Regel das am weitesten "Links".
Heisst: Nur die "oberen" 4 Bytes brauchen das Vorzeichen, die unteren nicht.
Was auch wiederum heisst: Der "untere" Teil gilt als "absoluter" Wert.

Spiel mal rum. Vielleicht wirds dann klarer.

Code: Alles auswählen

program Project1;
{$mode objfpc}{$H+}
Type
  TInt32Pair=packed record
    LowPart : Uint32;
    HighPart: Int32;
  end;
Const
  magic = 4294967295;
Var
  t:TInt32Pair;
begin
  t.LowPart:=-223372036854775807 And magic;
  t.HighPart:=-223372036854775807 shr 32;
  Writeln(t.HighPart,' | ',t.LowPart);
  Readln;
end.
EDIT: Ich glaube ich bin deinem Denkfehler auf die Schliche gekommen.
Dein Record soll eine "Repräsentation" einer 64-Bit Zahl sein.
Also musst du sie auch als solches behandeln.

Führ mal das hier aus:

Code: Alles auswählen

program Project1;
begin
  Writeln(BinStr(Int64(-200),20));
  Writeln(BinStr(Int64(200),20));
  Readln;
end.         

Wirst staunen....

Bottom Line: Bei so einer Repräsentation mit den "einzelnen" Teilen zu rechnen ist falsch.

EDIT2: Also die 4 Grundrechenarten gehen mit Comp (Was die Bits betrifft. Ja, "Darstellung" ist ein Float),
nur nicht DIV und MOD (Weil Operator not overloaded)
Ein System sie alle zu knechten, ein Code sie alle zu finden,
Eine IDE sie ins Dunkel zu treiben, und an das Framework ewig zu binden,
Im Lande Redmond, wo die Windows drohn.

alfware17
Beiträge: 210
Registriert: Di 14. Dez 2010, 23:27

Re: Wie rechne ich richtig mit TInt32Pair?

Beitrag von alfware17 »

Vielen Dank für die Erklärung mit dem Vorzeichen.
Und bezüglich COMP, also mein Benchmark war erstaunlich. Ich habe 1.000.000mal jeweils ADD, MUL, DIV, MOD (bzw umschrieben mit Restedivision) und "POT" (na gut, 3x MUL, vielleicht nehme ich noch 10) ausführen lassen und das jeweils für INT64 (Lazarus) bzw LONGINT (Turbo)
oder für COMP und die Zeit gemessen. Ich muß noch was an der Formatierung machen und die Ergebnisse numerisch prüfen aber der Trend ist schon mal zu sehen:
Bei Lazarus sind INT64 und COMP fast gleichauf, bei DIV, MOD und POT hat sogar COMP die Nase vorn, ich nehme an der Einfluß des Gleitkommas, das geht wohl in die FPU.
Bei Turbo hat COMP ganz eindeutig die Nase vorn, hier scheint mir der Einfluß des $N+ überwältigend. Nun habe ich eigentlich selten so große Multiplikationen aber beim Timestamp kann ich es ja mal probieren. Auf jeden Fall danke für die Anregung. Wenn ich meine Formatierungen gebändigt habe, zeige ich mal den Code, gesehen habe ich die 19/20 gültigen Stellen aber schon

PascalDragon
Beiträge: 945
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: Wie rechne ich richtig mit TInt32Pair?

Beitrag von PascalDragon »

alfware17 hat geschrieben: Di 25. Mär 2025, 10:21 Das Beispiel von PascalDragon habe ich mir angeschaut, aber ich fürchte Turbo Pascal hat kein dword und kein qword oder?
Der Code war als Inspiration gedacht, da für Emulation von 64-Bit Typen hier eben die 32-Bit Typen genutzt werden. Du müsstest das dann entsprechend anpassen, um das auf Basis von 16-Bit Typen zu implementieren. Wenn du sehr abenteuerlich bist, kannst du dir auch die entsprechenden Hilfsfunktionen anschauen, die von FPC für i8086 (als die 16-Bit x86 Plattform) genutzt werden (für 64-bit Typen hier und für 32-bit Typen hier), aber obacht, das ist alles Inline Assembly. ;) (und du müsstest sie halt anpassen, dass sie ein entsprechendes 32- oder 64-Bit Record als Parameter nutzen)
FPC Compiler Entwickler

alfware17
Beiträge: 210
Registriert: Di 14. Dez 2010, 23:27

Re: Wie rechne ich richtig mit TInt32Pair?

Beitrag von alfware17 »

Hallo ich war da noch einen Benchmark schuldig,
den ich nun gemacht habe:

Code: Alles auswählen

program Bench;

{$I OsWahl.inc}

{$IFDEF DOS} {$M 65520,0,655360} {$N+}
{$ELSE} {$mode objfpc} {$H+}
        {$IFDEF X86_64} {$MAXSTACKSIZE $7FFFFFF} {$ENDIF}
{$ENDIF}

uses
  Stdio, Zeit, Algebra;

const
  LBEZ = {$IFDEF DOS} 'LongInt' {$ELSE} 'Int64  ' {$ENDIF};
  Iterations = {$IFDEF DOS} 10000000 {$ELSE} 1000000000 {$ENDIF};

var
  i: longint;
  IntResult, IntA, IntB, IntF: Card;
  CompResult, CompA, CompB, CompF: Comp;
  SymbResult, SymbA, SymbB, SymbRest, SymbF, Symb11: ZAHL;
  Diff: array[0..18] of String;
  IterationsSymb :Card;

begin
  IntA := 987;
  IntB := 123;
  IntF := {$IFDEF DOS} 7 {$ELSE} 44 {$ENDIF};
  CompA := 987;
  CompB := 123;
  CompF := 44;
  IterationsSymb := Iterations div {$IFDEF DOS} 1000 {$ELSE} 10000 {$ENDIF};
  (* DOS  10.000.000 div 1.000  =  10.000 
     ELSE 1 Mrd      div 10.000 = 100.000 *)

  alCard2Zahl(987, SymbA);
  alCard2Zahl(123, SymbB);
  alCard2Zahl(44, SymbF);
  alCard2Zahl(11, Symb11);
  SymbResult := alNull;
  SymbRest := alNull;

  Intervall_Start(7);
  Intervall_Start(6);
  Intervall_Start(1);
  for i := 1 to Iterations do
    IntResult := IntA + IntB;
  Intervall_Stop(1);
  Writeln(StrCard(IntA),' + ', StrCard(IntB), ' = ', StrCard(IntResult));
  Diff[1] := Intervall_Dauer(1);
  writeln(LBEZ + ' Addition ', Diff[1], ' ms');

  Intervall_Start(2);
  for i := 1 to Iterations do
    IntResult := IntA * IntB;
  Intervall_Stop(2);
  Writeln(StrCard(IntA),' * ', StrCard(IntB), ' = ', StrCard(IntResult));
  Diff[2] := Intervall_Dauer(2);
  writeln(LBEZ + ' Multiplikation ', Diff[2], ' ms');

  Intervall_Start(3);
  for i := 1 to Iterations do
    IntResult := IntA div IntB;
  Intervall_Stop(3);
  Writeln(StrCard(IntA),' div ', StrCard(IntB), ' = ', StrCard(IntResult));
  Diff[3] := Intervall_Dauer(3);
  writeln(LBEZ + ' Division ', Diff[3], ' ms');

  Intervall_Start(4);
  for i := 1 to Iterations do
    IntResult := IntA mod IntB;
  Intervall_Stop(4);
  Writeln(StrCard(IntA),' mod ', StrCard(IntB), ' = ', StrCard(IntResult));
  Diff[4] := Intervall_Dauer(4);
  writeln(LBEZ + ' Modulo ', Diff[4], ' ms');

  Intervall_Start(5);
  for i := 1 to Iterations do
    IntResult := IntF * IntF * IntF * IntF * IntF * IntF * IntF * IntF * IntF * IntF * IntF;
  Intervall_Stop(5);
  Intervall_Stop(6);
  Writeln(StrCard(IntF),' ^ ', 11, ' = ', StrCard(IntResult));
  Diff[5] := Intervall_Dauer(5);
  writeln(LBEZ + ' Potenz ', Diff[5], ' ms');
  Diff[16]:=Intervall_Dauer(6);
  writeln(LBEZ + ' ', Diff[16], ' ms');

  Intervall_Start(6);
  Intervall_Start(1);
  for i := 1 to Iterations do
    CompResult := CompA + CompB;
  Intervall_Stop(1);
  Writeln(StrCompN(CompA),' + ', StrCompN(CompB), ' = ', StrCompN(CompResult));
  Diff[6] := Intervall_Dauer(1);
  writeln('Comp    Addition ', Diff[6], ' ms');

  Intervall_Start(2);
  for i := 1 to Iterations do
    CompResult := CompA * CompB;
  Intervall_Stop(2);
  Writeln(StrCompN(CompA),' * ', StrCompN(CompB), ' = ', StrCompN(CompResult));
  Diff[7] := Intervall_Dauer(2);
  writeln('Comp    Multiplikation ', Diff[7], ' ms');

  Intervall_Start(3);
  for i := 1 to Iterations do
    CompResult := CompA {$IFDEF WIN_64} div {$ELSE} / {$ENDIF} CompB;
  Intervall_Stop(3);
  Writeln(StrCompN(CompA),' / ', StrCompN(CompB), ' = ', StrCompN(CompResult));
  Diff[8] := Intervall_Dauer(3);
  writeln('Comp    Division ', Diff[8], ' ms');

  Intervall_Start(4);
  for i := 1 to Iterations do
    CompResult := CompA - (CompB * Trunc(CompA / CompB));
  Intervall_Stop(4);
  Writeln(StrCompN(CompA),' mod ', StrCompN(CompB), ' = ', StrCompN(CompResult));
  Diff[9] := Intervall_Dauer(4);
  writeln('Comp    Modulo ', Diff[9], ' ms');

  Intervall_Start(5);
  for i := 1 to Iterations do
    CompResult := CompF * CompF * CompF * CompF * CompF * CompF * CompF * CompF *CompF * CompF * CompF;
  Intervall_Stop(5);
  Intervall_Stop(6);
  Writeln(StrCompN(CompF),' ^ ', 11, ' = ', StrCompN(CompResult));
  Diff[10] := Intervall_Dauer(4);
  writeln('Comp    Potenz ', Diff[10], ' ms');
  Diff[17] := Intervall_Dauer(6);
  writeln('Comp    ', Diff[17], ' ms');

  Intervall_Start(6);
  Intervall_Start(1);
  for i := 1 to IterationsSymb do
    alAdd(SymbA, SymbB, SymbResult);
  Intervall_Stop(1);
  alWrite(SymbA); Write(' + '); alWrite(SymbB); Write(' = '); alWrite(SymbResult); Writeln;
  Diff[11] := Intervall_Dauer(1);
  writeln('Symb    Addition ', Diff[11], ' ms');

  Intervall_Start(2);
  for i := 1 to IterationsSymb do
    alMul(SymbA, SymbB, SymbResult);
  Intervall_Stop(2);
  alWrite(SymbA); Write(' * '); alWrite(SymbB); Write(' = '); alWrite(SymbResult); Writeln;
  Diff[12] := Intervall_Dauer(2);
  writeln('Symb    Multiplikation ', Diff[12], ' ms');

  Intervall_Start(3);
  for i := 1 to IterationsSymb do
    (* alDiv(SymbA, SymbB, SymbResult); *)
    alDivR(SymbA, SymbB, SymbResult, SymbRest);
  Intervall_Stop(3);
  alWrite(SymbA); Write(' / '); alWrite(SymbB); Write(' = '); alWrite(SymbResult); Writeln;
  Diff[13] := Intervall_Dauer(3);
  writeln('Symb    Division ', Diff[13], ' ms');

  Intervall_Start(4);
  for i := 1 to IterationsSymb do
    (* alMod(SymbA, SymbB, SymbResult); *)
    SymbResult := SymbRest;
  Intervall_Stop(4);
  alWrite(SymbA); Write(' mod '); alWrite(SymbB); Write(' = '); alWrite(SymbResult); Writeln;
  Diff[14] := Intervall_Dauer(4);
  writeln('Symb    Modulo ', Diff[14], ' ms');

  Intervall_Start(5);
  for i := 1 to IterationsSymb do
    alPot(SymbF, Symb11, SymbResult);
  Intervall_Stop(5);
  Intervall_Stop(6);
  Intervall_Stop(7);
  alWrite(SymbF); Write(' ^ '); alWrite(Symb11); Write(' = '); alWrite(SymbResult); Writeln;
  Diff[15] := Intervall_Dauer(5);
  writeln('Symb    Potenz ', Diff[15], ' ms');
  Diff[18] := Intervall_Dauer(6);
  writeln('Symb    ', Diff[18], ' ms');

  Writeln('Vergleich Addition ');
  Writeln('   ' + LBEZ + ' ', Diff[1], ' ms ');
  Writeln('   ' + 'Comp    ', Diff[6], ' ms ');
  Writeln('   ' + 'Symb    ', Diff[11], ' ms');
  Writeln('Vergleich Multiplikation ');
  Writeln('   ' + LBEZ + ' ', Diff[2], ' ms ');
  Writeln('   ' + 'Comp    ', Diff[7], ' ms ');
  Writeln('   ' + 'Symb    ', Diff[12], ' ms');
  Writeln('Vergleich Division ');
  Writeln('   ' + LBEZ + ' ', Diff[3], ' ms ');
  Writeln('   ' + 'Comp    ', Diff[8], ' ms ');
  Writeln('   ' + 'Symb    ', Diff[13], ' ms');
  Writeln('Vergleich Modulo ');
  Writeln('   ' + LBEZ + ' ', Diff[4], ' ms ');
  Writeln('   ' + 'Comp    ', Diff[9], ' ms ');
  Writeln('   ' + 'Symb    ', Diff[14], ' ms');
  Writeln('Vergleich Potenz ');
  Writeln('   ' + LBEZ + ' ', Diff[5], ' ms ');
  Writeln('   ' + 'Comp    ', Diff[10], ' ms ');
  Writeln('   ' + 'Symb    ', Diff[15], ' ms');
  Writeln('Vergleich Gesamt ');
  Writeln('   ' + LBEZ + ' ', Diff[16], ' ms ');
  Writeln('   ' + 'Comp    ', Diff[17], ' ms ');
  Writeln('   ' + 'Symb    ', Diff[18], ' ms');

  Writeln(StrCard(Iterations), ' Iterationen ', Intervall_Dauer(7));
  readln;
end.
Das Ergebnis für mich etwas überraschend, COMP schneidet im 16-bit (Turbo-Pascal, getestet in einer Virtualbox mit WinXP)
sehr gut ab, vermutlich weil die FPU genutzt wird und da eigentlich im Gleitkomma-Modus geschummelt wird. Aber gut,
wenn es das gibt, kann man es nutzen. Für Free-Pascal waren COMP und INT64 etwa gleichauf und ja mir ist schon klar,
daß COMP nur für WIN_64 doch wieder ein INT64 ist.
Warum das so lange gedauert hat - nun ich habe
a) gleich meine erträumte Lösung, ein Windows-Timestamp auch im Turbo-Pascal, obwohl der nun eindeutig über 2^32 hinausgeht und
b) ich wollte gleich noch meine symbolische Mathematik mal mitlaufen lassen.
Die hat ja nun mit Stellenanzahl und Genauigkeit überhaupt kein Problem, allerdings ist der Benchmark dann nicht ganz fair weil ja
intern für ein einfaches Add ganz viele Operationen ablaufen. Ich denke ich habe aber einen fairen Maßstab gefunden. Ich habe sie auch
mal auf 3000 Stellen beschnitten, weil sonst wurde sie zu langsam aber das ist vermutlich eine andere Baustelle.

Code: Alles auswählen

Laufzeit WinXP/Turbo-Pascal:
987 + 123 = 1.110
LongInt Addition Dauer:00:00:00,00 ms
987 * 123 = 121.401
LongInt Multiplikation Dauer:00:00:00,33 ms
987 div 123 = 8
LongInt Division Dauer:00:00:00,38 ms
987 mod 123 = 3
LongInt Modulo Dauer:00:00:00,44 ms
7 ^ 11 = 1.977.326.743
LongInt Potenz Dauer:00:00:03,30 ms
LongInt Dauer:00:00:04,45 ms
987 + 123 = 1.110
Comp    Addition Dauer:00:00:00,05 ms
987 * 123 = 121.401
Comp    Multiplikation Dauer:00:00:00,00 ms
987 / 123 = 8
Comp    Division Dauer:00:00:00,11 ms
987 mod 123 = 3
Comp    Modulo Dauer:00:00:00,44 ms
44 ^ 11 = 1.196.683.881.290.399.740
Comp    Potenz Dauer:00:00:00,44 ms
Comp    Dauer:00:00:00,76 ms
987 + 123 = 1.110
Symb    Addition Dauer:00:00:00,06 ms
987 * 123 = 121.401
Symb    Multiplikation Dauer:00:00:00,11 ms
987 / 123 = 8
Symb    Division Dauer:00:00:00,22 ms
987 mod 123 = 3
Symb    Modulo Dauer:00:00:00,00 ms
44 ^ 11 = 1.196.683.881.290.399.744
Symb    Potenz Dauer:00:00:01,98 ms
Symb    Dauer:00:00:02,37 ms
Vergleich Addition
   LongInt Dauer:00:00:00,00 ms
   Comp    Dauer:00:00:00,05 ms
   Symb    Dauer:00:00:00,06 ms
Vergleich Multiplikation
   LongInt Dauer:00:00:00,33 ms
   Comp    Dauer:00:00:00,00 ms
   Symb    Dauer:00:00:00,11 ms
Vergleich Division
   LongInt Dauer:00:00:00,38 ms
   Comp    Dauer:00:00:00,11 ms
   Symb    Dauer:00:00:00,22 ms
Vergleich Modulo
   LongInt Dauer:00:00:00,44 ms
   Comp    Dauer:00:00:00,44 ms
   Symb    Dauer:00:00:00,00 ms
Vergleich Potenz
   LongInt Dauer:00:00:03,30 ms
   Comp    Dauer:00:00:00,44 ms
   Symb    Dauer:00:00:01,98 ms
Vergleich Gesamt
   LongInt Dauer:00:00:04,45 ms
   Comp    Dauer:00:00:00,76 ms
   Symb    Dauer:00:00:02,37 ms
10.000.000 Iterationen Dauer:00:00:07,58

Code: Alles auswählen

Laufzeit Win10, FreePascal64, 32 sah ähnlich aus
987 + 123 = 1.110
Int64   Addition Dauer:00:00:01,805 ms
987 * 123 = 121.401
Int64   Multiplikation Dauer:00:00:01,783 ms
987 div 123 = 8
Int64   Division Dauer:00:00:08,546 ms
987 mod 123 = 3
Int64   Modulo Dauer:00:00:08,538 ms
44 ^ 11 = 1.196.683.881.290.399.744
Int64   Potenz Dauer:00:00:02,839 ms
Int64   Dauer:00:00:23,511 ms
987 + 123 = 1.110
Comp    Addition Dauer:00:00:01,789 ms
987 * 123 = 121.401
Comp    Multiplikation Dauer:00:00:01,767 ms
987 / 123 = 8
Comp    Division Dauer:00:00:08,530 ms
987 mod 123 = 3
Comp    Modulo Dauer:00:00:06,796 ms
44 ^ 11 = 1.196.683.881.290.399.744
Comp    Potenz Dauer:00:00:06,796 ms
Comp    Dauer:00:00:21,712 ms
987 + 123 = 1.110
Symb    Addition Dauer:00:00:00,062 ms
987 * 123 = 121.401
Symb    Multiplikation Dauer:00:00:00,570 ms
987 / 123 = 8
Symb    Division Dauer:00:00:01,003 ms
987 mod 123 = 3
Symb    Modulo Dauer:00:00:00,016 ms
44 ^ 11 = 1.196.683.881.290.399.744
Symb    Potenz Dauer:00:00:08,222 ms
Symb    Dauer:00:00:09,873 ms
Vergleich Addition
   Int64   Dauer:00:00:01,805 ms
   Comp    Dauer:00:00:01,789 ms
   Symb    Dauer:00:00:00,062 ms
Vergleich Multiplikation
   Int64   Dauer:00:00:01,783 ms
   Comp    Dauer:00:00:01,767 ms
   Symb    Dauer:00:00:00,570 ms
Vergleich Division
   Int64   Dauer:00:00:08,546 ms
   Comp    Dauer:00:00:08,530 ms
   Symb    Dauer:00:00:01,003 ms
Vergleich Modulo
   Int64   Dauer:00:00:08,538 ms
   Comp    Dauer:00:00:06,796 ms
   Symb    Dauer:00:00:00,016 ms
Vergleich Potenz
   Int64   Dauer:00:00:02,839 ms
   Comp    Dauer:00:00:06,796 ms
   Symb    Dauer:00:00:08,222 ms
Vergleich Gesamt
   Int64   Dauer:00:00:23,511 ms
   Comp    Dauer:00:00:21,712 ms
   Symb    Dauer:00:00:09,873 ms
1.000.000.000 Iterationen Dauer:00:00:55,096
Bleibt als Erkenntnis: Das TInt32Pair ist mir zu schwer und ich brauche es auch nicht.
FPC32 hat ja den Int64 und für Turbo-Pascal(16) kann ich COMP nehmen, aber bitte nur dort, sonst wird es wieder zu haarig.
Dateianhänge
extended.zip
(75.31 KiB) 56-mal heruntergeladen
benchmark.zip
(595.25 KiB) 61-mal heruntergeladen

Antworten