Berechnung mit großen Zahlen. Variablenüberlauf?
-
Hallo,
in meinem Script werden Schwerpunkte zwischen zwei Körpern berechnet und ich verstehe nicht das Ergebnis, dass das Script bei mir ausgibt.
Ein Beispiel mit Zahlen:int xj, yj; long Vi, Vj; int xi = 1059; int yi = 1041; xj = 951; yj = 957; Vi = 109239; Vj = 2162117; long new_V = Vi + Vj; unsigned int new_x = ((Vi*xi+Vj*xj) / new_V); unsigned int new_y = ((Vi*yi+Vj*yj) / new_V); cout <<new_x <<" ; " <<new_y <<endl; float new_x_f = ((Vi*xi+Vj*xj) / new_V); float new_y_f = ((Vi*yi+Vj*yj) / new_V); cout <<new_x_f <<" ; " <<new_y_f <<endl; unsigned long new_x_l = ((Vi*xi+Vj*xj) / new_V); unsigned long new_y_l = ((Vi*yi+Vj*yj) / new_V); cout <<new_x_l <<" ; " <<new_y_l <<endl; unsigned long long new_x_ll = ((Vi*xi+Vj*xj) / new_V); unsigned long long new_y_ll = ((Vi*yi+Vj*yj) / new_V); cout <<new_x_ll <<" ; " <<new_y_ll <<endl; new_x = ((Vi*xi/ new_V + Vj*xj/ new_V) ); new_y = ((Vi*yi/ new_V + Vj*yj/ new_V) ); cout <<new_x <<" ; " <<new_y <<endl;
Wenn ich diesen Code ausführe erschein in der Ausgabe:
4294966362 ; 4294966367
-934 ; -929
4294966362 ; 4294966367
18446744073709550682 ; 18446744073709550687
955 ; 960Die letzte Zeile mit 955 und 960 sind die richtigen Zahlen aber wie kommen die anderen Zahlen zustande?
Vielen Dank für die Hilfe und Grüße
Stefan
-
@st3fan85 sagte in Berechnung mit großen Zahlen. Variablenüberlauf?:
Welchen Compiler auf welchem System nutzt du denn? Prinzipiell kann das funktionieren: https://godbolt.org/z/YWx7qq
Aber
(Vi*xi+Vj*xj)
ergibt in dem Beispiel natürlich eine sehr große Zahl, die in einen 32bit integer nicht rein passt. Du könntestuint64_t
nehmen, dass ist grantiert 64 bit groß.
-
Hi,
ich verwende MinGW auf Windows. In dem Paketmanager von MinGW ist der Gcc32 vermerkt. Heißt wohl die 32bit Version von GCC?
Ich habe es mit uint64_t versucht:int xj, yj; long Vi, Vj; int xi = 1059; int yi = 1041; xj = 951; yj = 957; Vi = 109239; Vj = 2162117; long new_V = Vi + Vj; uint64_t new_x = ((Vi*xi+Vj*xj) / new_V); uint64_t new_y = ((Vi*yi+Vj*yj) / new_V); cout <<new_x <<" ; " <<new_y <<endl; new_x = ((Vi*xi/ new_V + Vj*xj/ new_V) ); new_y = ((Vi*yi/ new_V + Vj*yj/ new_V) ); cout <<new_x <<" ; " <<new_y <<endl; }
Ausgabe:
18446744073709550682 ; 18446744073709550687
955 ; 960Das Problem ist, wenn die Größe der Zahlen sich nur um eine Größenordnung erhöht, dann reicht auch die Methode mit dem getrennten dividieren nicht mehr aus. Und die Zahlen werden sich noch um mehr als eine Größenordnung erhöhen.
Ist hier die Grenze von MinGW erreicht?
-
@st3fan85
Auch MinGW-64Bit bietet den (Nicht-Standard) Datentyp __uint128_t an, das sollte für deine Rechnungen erstmal reichen.
Schwieriger wird es dabei aber mit printf&Co.
Mein Compiler zB. heißt x86_64-w64-mingw32-gcc.exe, und der ist 64Bit.
-
@st3fan85 Aufgrund der
Auswertungsreihenfolgeverwendeten Datentypen will der Compiler(Vi*xi+Vj*xj)
trotzdem erstmal zu einem 32 bit Integer Auswerten,weil. Wenn duxj
undyj
32 bit groß sinddieirgendwas davon zuint64_t
machst, denke ich, dass die Zahlen aus deinem Beispiel funktionieren
-
Problem bei
uint64_t new_x = ((Vi*xi+Vj*xj) / new_V);
ist, daß der Ausdruck zuerst mitlong
(als größter Datentyp der benutzen Variablen) berechnet wird und danach erst das Ergebnis davon in einuint64_t
gecastet wird.Man müßte also z.B. auch die verwendeten Variablen schon als
uint64_t
deklarieren.
-
Natürlich, dass was @Th69 sagt.
-
@Schlangenmensch Könnt Ihr euch mal angewöhnen Konjunktionen und Relativpronomen nicht durcheinander zu bringen? Das tut weh und Du bist schon der zweite hier in diesem Thread.
-
@Th69 sagte in Berechnung mit großen Zahlen. Variablenüberlauf?:
uint64_t new_x = ((Vi*xi+Vj*xj) / new_V);
uint64_t new_x = ((int64_t{Vi}*xi+int64_t{Vj}*xj) / new_V);
Alternativ: verwende gleich
int64_t
als Typ fürVi
undVj
.long
macht in den wenigsten Fällen Sinn, weil es auf sehr üblichen Compilern/Plattformen eben 32 oder auch 64 Bit sein kann (je nachdem ob du für 32 oder 64 Bit kompilierst).long
nehme ich nur wenn ich mit einer API arbeite dielong
verwendet (z.B. dielong
irgendwo zurückgibt und dann den selben Wert wieder irgendwo als Parameter haben will).
-
@hustbaer: Das habe ich extra nicht vorgeschlagen, da es sehr unleserlich ist...
-
@Th69
Unleserlich ist relativ
Aber ja, es wäre vermutlich besser einfachint64_t
überall zu verwenden.
Für Storage kann man es ja wenn nötig auf kleinere Typen runterkonvertieren, aber bei der Berechnung hat man auf 64 Bit Plattformen kaum einen Vorteil wenn man kleinere Typen für die Variablen verwendet.
-
@hustbaer sagte in Berechnung mit großen Zahlen. Variablenüberlauf?:
Für Storage kann man es ja wenn nötig auf kleinere Typen runterkonvertieren, aber bei der Berechnung hat man auf 64 Bit Plattformen kaum einen Vorteil wenn man kleinere Typen für die Variablen verwendet.
Es wären durchaus ein paar SIMD-ähnliche Optimierungen in allgemeinen Registern denkbar, wenn noch Platz für weitere Werte ist - auch ohne Instruktionen einer SIMD-Befehlssatzerweiterung zu verwenden. Stichwort SWAR. Gibt ein paar Bitschubser-Algorithmen, die diesbezüglich gekonnt tricksen.
Ob aber auch Compiler sowas machen weiss ich nicht - hat das schonmal jemand von euch beobachtet? Man könnte ja schon z.B. zwei 32-Bit-Additionen als eine 64-Bit-Additon umsetzen, wenn man die Überläufe manuell behandelt.
-
Hallo,
vielen Dank für die Hilfe! Ich verwende jetzt alle Variablen als Typuint64_t
.
Zur Info, sobald zwei Variablen aus der Gleichung(Vi*xi+Vj*xj)
inuint64_t
vorhanden sind, passt das Ergebnis.