unsigned in Berechnungen
-
Hallo Zusammen,
eine Frage zu unsigned in Berechnungen.
Ich habe gelesen, dass wenn unsigned int und int gemischt in einer Berechnung durchgeführt werden, der int typ auf unsigned int 'promoted' wird.
Also zum Beispiel:
unsigned u = 100; int i = -1 * u;
i hat jetzt dennoch den erwarteten Wert von -1. Im Endeffekt steht hier jetzt ja:
(int)((unsigned)-1 * u);
Aber ist diese Berechnung jetzt sicher - im Sinne von immer definiert - oder undefiniert? Allgemeiner gefragt, kann man unsigned und int in Berechnungen stets sicher miteinander kombinieren (solange der typ dem das Ergebnis zugewiesen hat passt)?
Das "Dilemma" mit der 'promotion' von int -> unsigned sieht man hier ganz schön, da kein cast auf int mehr erfolgt:
unsigned u = 100; int i = -1; cout << u * i << endl; // => 4294967196
Vielen Dank schon mal!
-
starseed84 schrieb:
i hat jetzt dennoch den erwarteten Wert von -1. Im Endeffekt steht hier jetzt ja:
(int)((unsigned)-1 * u);
Die Konvertierung unsigned->signed ist für zu große Werte implementation defined. Falls dein Computer 2er-Komplementdarstellung benutzt, ist das Ergebnis allerdings so zu erwarten: bei der Multiplikation von 2 n-Bit-Zahlen unterscheiden sich die niederwertigen n Bits des Ergebnisses nicht, egal ob es sich um unsigned oder signed Zahlen in 2er-Komplementdarstellung handelt.
starseed84 schrieb:
Allgemeiner gefragt, kann man unsigned und int in Berechnungen stets sicher miteinander kombinieren (solange der typ dem das Ergebnis zugewiesen hat passt)
Im Allgemeinen nicht. Betrachte zum Beispiel die Division. Ja hingegen bei Beschränkung auf Addition, Subtraktion und Multiplikation in 2er-Komplementdarstellung (und wenn alle Operationen mit der gleichen Anzahl an Bits ausgeführt werden).
Faustregel: Lass es. Hier unpräzise zu sein führt nur zu schwer auffindbaren Fehlern.
-
Hallo Camper,
danke für deine Antwort. Das bestätigt meine Befürchtungen.
Bisher fand ich es immer ganz schick unsigned dort zu nutzen, wo man keine negativen Zahlen erwartet. Nehmen wir als Beispiel:
int personalKosten(unsigned anzahlMitarbeiter) { return anzahlMitarbeiter * -2500; }
Wie würde denn ein vernünftiges Funktions- / Klassen-Design anstatt dessen aussehen? Sollte man alle Paramter / Datentypen als int gestalten und dafür dann auf >= 0 prüfen? Denn sobald man unsigned als paramter verwendet, besteht m.E. immer die latente Gefahr, dass man es mal mit signed mischen könnte.
int personalKosten(int anzahlMitarbeiter) { if (anzahlMitarbeiter >= 0) return anzahlMitarbeiter * -2500; return 0; }
Viele Grüße!
PS: Das Beispiel für einen Parameter der typischerweise nicht kleiner 0 ist, habe ich mir jetzt schnell aus den Fingern gesaugt, bitte nicht auf die Goldwaage legen
-
starseed84 schrieb:
Wie würde denn ein vernünftiges Funktions- / Klassen-Design anstatt dessen aussehen? Sollte man alle Paramter / Datentypen als int gestalten und dafür dann auf >= 0 prüfen? Denn sobald man unsigned als paramter verwendet, besteht m.E. immer die latente Gefahr, dass man es mal mit signed mischen könnte.
int personalKosten(int anzahlMitarbeiter) { if (anzahlMitarbeiter >= 0) return anzahlMitarbeiter * -2500; return 0; }
Also ich nehme in solch einer Situation int. Ich fühle mich nicht dafür verantworlich, wenn der Nutzer zu doof ist, sich die Doku zur Schnittstelle anzuschauen. Wenn der Nutzer die Funktion fehlerhaft nutzt, ist das sein Pech. Du kümmerst dich nicht um Implementierungsdetails deiner Nutzer und schreibst in die Doku nur, wie die Funktion verwendet wird.
-
starseed84 schrieb:
Denn sobald man unsigned als paramter verwendet, besteht m.E. immer die latente Gefahr, dass man es mal mit signed mischen könnte.
ein anderes problem ist ein semantisches: mit
unsigned
kannst du dir sicher sein, was bei einem overflow passiert (wrap-around). beiint
führt das zu undefiniertem verhalten. damit handelst du dir allerdings implizit modulo-arithmetik mitunsigned
ein, was wahrscheinlich nicht ist, was du wolltest und im schlimmsten fall auch auf die performance geht. meine faustregel lautet daher: im zweifelsigned
. statt ein bit durchunsigned
zu gewinnen, lieber gleich auflong
oderlong long
wechseln. alles andere zahlt sich nicht aus.