Unverständliches Rechenergebnis (Datentypen?)
-
Hallo liebe C++-Gemeinde

Ich habe ein weiteres Problem, das sich auf folgenden Code reduzieren lässt:
#include <iostream> using namespace std; int main() { unsigned long a(4); unsigned long b(10); double faktor(1.2); long ergebnis; ergebnis = (a - b) * faktor; cout << "Ergebnis: " << ergebnis << endl; return 0; }Das Ergebnis ist nicht wie erwartet -7, sondern -2147483648.
Ich bin mir ziemlich sicher, dass der Fehler in der Umrechnung der verschiedenen Datentypen liegt.
Da (a - b) als Ergebnis einen unsigned long liefert, wird das Ergebnis falsch - komischerweise aber erst, wenn es mit einem Gleitkomma-Typ verrechnet wird - die direkte Umwandlung in ein signed long funktioniert hingegen.Was kann ich denn dagegen tun? (a - b) explizit in einen signed long umwandeln?
Viele Grüße,
lyrichter
-
Hallo,
das Problem liegt darin, dass deine Rechnung (a-b)= -6 ergibt, und das nicht im Wertebereich eines unsigned long liegt. Muss dein Long tatsächlich unsigned sein? Wie du schon selbst festgestellt hast, tritt der Fehler nicht bei signed long auf...
Noch eine Idee von mir: Wenn es sich um unsigned long drehen MUSS, UND du weißt, dass b größer als a ist, dann schreib deine Problem-Rechnung doch so:
ergebnis = - ((b - a) * faktor);Kleiner Nachtrag: Versuch mal das folgende:
ergebnis = (static_cast<long>(a) - static_cast<long>(b)) * faktor
-
Hey,
ist dir denn überhaupt klar, wozu das Keyword
unsignedüberhaupt gut ist?Du sagst ja damit, dass die Zahlen a und b vorzeichenlos sind und somit nur positive Werte erhalten können. Das Ergebnis dieses Ausdrucks ist somit klar definiert.
unsigned <operator> unsigned = unsigned
Nun kommt bei deiner Berechnung aber ein negativer Wert heraus, welcher nicht dem erwarteten Datentyp von unsigned entspricht und auch nicht in diesen konvertiert werden kann. Das Verhalten ist an dieser Stelle undefiniert.
Du könntest das so lösen:
ergebnis = (static_cast<long>(a) - static_cast<long>(b)) * faktor;Damit hast du als Ergebnis aber noch immer nicht das korrekte Ergebnis. Schließlich ist ergebnis ein long. Und ein long ist ein ganzzahliger Datentyp, und kann somit keine Nachkommastellen speichern.
So würde es also gehen:
#include <iostream> using namespace std; int main() { unsigned long a(4); unsigned long b(10); double faktor(1.2); double ergebnis; ergebnis = (static_cast<long>(a) - static_cast<long>(b)) * faktor; cout << "Ergebnis: " << ergebnis << endl; return 0; }Um die Berechnung nochmal zu erläutern. Zunächst wird natürlich die Klammer ausgewertet. Dabei handelt es sich um ein
unsigned long, welcher in einsigned longkonvertiert wird und anschließend von einemunsigned long, der aber auch in einsigned longkonvertiert wird subtrahiert wird.Da also nun beide Variablen vom Typ
signed longsind, ist auch das Ergebnis dieses Ausdruckssigned long.Anschließend wird das Ergebnis vom Typ
signed longmit einemdoublemultipliziert. Das Ergebnis dieses Ausdrucks ist somit auch eindouble.
-
Erst einmal vielen Dank für die Antworten!
Nun habe ich eingie Rückfragen/Anmerkungen:
ist dir denn überhaupt klar, wozu das Keyword unsigned überhaupt gut ist?
Ja.
Wenn es sich um unsigned long drehen MUSS, UND du weißt, dass b größer als a ist
Ja, es muss laut Aufgabenstellung, aber ich weiß im Voraus leider nicht, ob a oder b größer ist.
Wie du schon selbst festgestellt hast, tritt der Fehler nicht bei signed long auf...
Das habe ich zwar auch festgestellt, war aber nicht das, was ich in meinem Eingangspost meinte. Ich wundere mich nur darüber, dass folgender Code funktioniert, da (a - b) ja vom Typ unsigned long sein sollte.
unsigned long a(4); unsigned long b(10); long ergebnis; ergebnis = (a - b);Die Umwandlung von unsigned long nach long wird hier dann wohl automatisch vorgenommen und funktioniert hier. Sogar so gut, dass aus einem Wert ohne Vorzeichen der korrekte Wert mit Vorzeichen wird, was mich wie gesagt sehr wundert.
Außerdem funktioniert auch
unsigned long a(4); long b(-10); long ergebnis; ergebnis = a + b;Das kann ich mir ebenfalls nicht erklären, da
long + unsigned long --> unsigned long
und ein unsigned long dann wieder in ein korrektes signed long mit Vorzeichen umgewandelt wird.Damit hast du als Ergebnis aber noch immer nicht das korrekte Ergebnis. Schließlich ist ergebnis ein long. Und ein long ist ein ganzzahliger Datentyp, und kann somit keine Nachkommastellen speichern.
Mein Fehler: Es sollte nicht, wie ich erst fälschlcherweise geschrieben habe, 7.2 in ergebnis gespeichert werden, sondern natürlich 7. Das Abschneiden der Nachkommastellen durch die Umwandlung ist ok und eingeplant.
Ich merke aber schon, dass das alles (wieder mal) etwas aus dem Ruder läuft. Anstatt mein problem total zu isolieren, hier mehr oder minder das Original-Problem:
#include <iostream> #include <cmath> #define PI 3.14159265 using namespace std; main() { unsigned long x(2), y(15); unsigned long zx(1), zy(19); double winkel(30); double rad; long new_x, new_y; rad = winkel * PI/180; new_x = (x - zx) * cos(rad) - (y - zy) * sin(rad) + zx; new_y = (y - zy) * cos(rad) + (x - zx) * sin(rad) + zy; cout << new_x << endl; cout << new_y << endl; }Im Moment kommt da noch völliger Unsinn heraus. Die korrekte Größenordnung dürfte new_x = 4, new_y = 16 sein.
Wie gehe ich jetzt am besten vor? Ist es sinnvoll, einfach wie oben von ,,,,, beschrieben vorzugehen, nur jetzt bei den beiden summanden?
-
du musst nicht vorher wissen we4r größer ist ... das kann man testen und die dementsprechende Methode wählen ...
-
Vorsicht. Dass die Umwandlung so funktioniert, wie du es erwartest, ist im C++-Standard nicht garantiert. Auf PCs funktioniert es deshalb, weil dort Integer in Zweierkomplementdarstellung gespeichert werden, d.h. beispielsweise bei 8-Bit-Integern:
0111 1111 = 127 1000 0000 = -128 (bzw. 128, wenn vorzeichenlos interpretiert) 1111 1111 = -1 (bzw. 255, wenn vorzeichenlos interpretiert)Die Umwandlung zwischen vorzeichenbehaftet und vorzeichenlos ist dann rein konzeptionell, und der Wertebereich wickelt sich sowohl bei vorzeichenlosen als auch bei vorzeichenbehafteten Integern herum. Jetzt ist das zwar auf heutigen Architekturen allgemein üblich, es hat aber durchaus auch schon mal andere Darstellungsarten gegeben.
Vom Standard garantiert ist: Vorzeichenlose Integer rechnen modulo 2^Breite. Das Verhalten der Zuweisung in einen vorzeichenbehafteten Integer außerhalb seines Wertebereichs ist dagegen nicht definiert.
Um also auf der sicheren Seite zu sein, musst du die Operanden der Differenz in vorzeichenbehaftete longs casten (und sicherstellen, dass ihre Werte davon dargestellt werden können).
-
padreigh schrieb:
du musst nicht vorher wissen we4r größer ist ... das kann man testen und die dementsprechende Methode wählen ...
Ja, aber das finde ich etwas umständlich mit einem langen Ausdruck wie diesem.
Gibt es keine andere Möglichkeit mit einigen geschickten Typumwandlungen?Oder ist das tatsächlich die beste Lösung?
Edit:
@seldon: Huch, deinen Beitrag hatte ich völlig übersehen.seldon schrieb:
Um also auf der sicheren Seite zu sein, musst du die Operanden der Differenz in vorzeichenbehaftete longs casten
ergebnis = (static_cast<long>(a) - static_cast<long>(b)) * faktor;Du meinst so? Und (long * double) umgewandelt in long ist sicher?
seldon schrieb:
(und sicherstellen, dass ihre Werte davon dargestellt werden können).
Du meinst, sicherstellen, dass die Operanden nicht zu groß sind? Wie kann ich das tun?
Viele Grüße,
lyrichter
-
So, das Programm funktioniert jetzt, nur leider ohne Schutz vor Überlaufen bei der Konvertierung von unsigned long nach signed long.
Eine andere Frage ist dabei aber aufgetaucht: Sind die beiden nachfolgenden Codes äquivalent?
unsigned long bla(5); long blub(bla);unsigned long bla(5); long blub; blub = static_cast<long>(bla);
-
Beides gleich beschissen...
Schreib einfach
ergebnis = (a>b?a-b:b-a) * faktor * (a>b?1:-1);dann hast du das Problem gar nicht.