integer Verständnis

Gast #1525612
Tag miteinander,

mal angenommen ich habe zwei Integer, mit denen ich eine Berechnung 
durchführen möchte. Darf dann ein Zwischenergebnis den Maximalwert eines 
Integers überschreiten?

Um mich deutlicher auszudrücken:
1
int16_t a = 521;
2
int16_t b = 732;
3

4
int16_t erg = (a * b)/100;

Es geht nicht darum, dass bei dem Ergebnis die Nachkommastellen 
abgeschnitten werden, sondern darum, ob nach der Multiplikation mit dem 
richtigen Zwischenergebnis weitergerechnet wird.

Wenn ja, dann warum?
Wenn nein, wie verhindere ich die Probleme am elegantesten?

Gruß
Gast #1525620
>Darf dann ein Zwischenergebnis den Maximalwert eines
>Integers überschreiten?
Ja.

>ob nach der Multiplikation mit dem
>richtigen Zwischenergebnis weitergerechnet wird
Nein.

>warum?
Hmmm. Na ein Integer ist nunmal nicht grösser als es gross ist. 16 Bit 
bleiben 16 Bit.

>wie verhindere ich die Probleme
Drei Alternativen.
1. Von vorne herein die Ausgangsvariablen so gross wählen, dass evtl. 
Zwischenergebnisse noch hineinpassen.
2. Rechnung so umstellen, das Zwischenergebnisse klein genug bleiben.
3. Variablen bzw. Ausdruck auf grösseren Typ casten (und ggf. das 
Ergebnis wieder zurück)
Gast #1525627
>wie verhindere ich die Probleme

Zwinge den Compiler, die Berechnung mit einem größeren Datentypen 
durchzuführen:
1
int16_t erg;
2
erg = (int16_t)  (  (int32_t) a  *  (int32_t) b  );
3
erg /= 100;
Gast #1525633
Oder ordne (wenn möglich) die Rechenschritte anders an.
Ansonsten gäbe es da noch die Integerpromotion, die kleine Ints 
automatisch aufbläst. Dazu müsste man allerdings wissen, wie groß ein 
natürlicher Int auf der Plattform ist.
Gast #1525666
>Oder ordne (wenn möglich) die Rechenschritte anders an.
Das wäre Alternative Nr. 2 von mir.

Sich auf Integer Promotion zu "verlassen" um gültige Zwischenwerte zu 
erreichen ist von den anzustellenden Überlegungen her gleichwertig zu 
Nr. 3.

Das Problem damit ist, das die Ursache für den Vorgang im Compiler 
nichts mit Deinem Problem der Zwischenwerte zu tun hat. Ein Aspekt ist, 
das wenn Datentypen verwendet werden, die kleiner als der "typische" 
Maschinenwert sind, eine Erweiterung auf den nächstgrösseren Integer Typ 
erfolgt. Ob ein Operator in dem Zusammenhang einen Überlauf (oder 
Unterlauf) erzeugen könnte spielt dabei keine Rolle. Siehe K&R, 2. 
Auflage, S.189.
Der zweite Aspekt der damit unter "Integer Promotion" zusammengefasst 
wird, hängt nur vom Operator und den Typen der Operanden ab. Wieder 
nicht von der Frage ob ein Überlauf stattfindet (oder stattfinden 
könnte). Siehe K&R, 2. Auflage, S.191

Der wesentliche Zweck dieser "Integer Promotion" ist es, die notwendigen 
Implementierungen für die Operatoren zu verringern. Es werden die 
Datentypen vereinheitlicht. D.h. statt z.B. eine 
short-int-Multiplikation zu implementiereren wandelt man (das ist nur 
ein Beispiel) das short in ein int um.

Am besten wirst Du das als Anfänger mal lesen, aber ansonsten ignorieren 
und entweder durch entsprechende Typwahl oder casts die Überläufe 
verhindern.
Gast #1525713
>Ein mal (int32_t) reicht aber auch ;) Nur ein Operand muss nach int32_t
>gecastet werden, damit die Operation 32bittig ausgeführt wird.

Ich weiß, aber das sieht schöner aus.


;-)
Gast #1525722
>Bisher wird einfach nur in 32 Bit multipliziert
>und dann wieder auf 16 Bit gestutzt.
Nein. Wie kommst Du darauf? Am besten Verweis auf die entsprechende K&R 
Seite.
Persönliche Seite #1525731
Mmmh schrieb:
>>Bisher wird einfach nur in 32 Bit multipliziert
>>und dann wieder auf 16 Bit gestutzt.
> Nein. Wie kommst Du darauf? Am besten Verweis auf die entsprechende K&R
> Seite.

Das würde mich stark wundern, wenn das nicht so wäre.
Die Zeile
1
erg /= 100
enthält keinen Cast nach 32 Bit, erg ist aber 16 Bit. Ergo wird die 
Division in 16 Bit ausgeführt.
Gast #1525792
Fängt das Problem nicht eigentlich schon eine Zeile früher an?
1
erg = (int16_t)  (  (int32_t) a  *  (int32_t) b  );
Hier wird das Zwischenergebnis auf 16 bit gecastet, obwohl es durchaus 
(wie z.B. in dem ursprünglichen Beispiel) größer als 16 bit sein kann.
Gast #1525905
A:
1
erg = (int16_t)  (  (int32_t) a  *  (int32_t) b  );

Jo, sehe ich das auch.
Mit den Zahlen von oben:
1
int16_t a = 521; // 0x209
2
int16_t b = 732; // 0x2DC
3

4
int16_t erg = (a * b)/100;

Dieser Zwischencast auf 32 Bit bringt bei diesem Ausdruck nix, da ja 
danach wieder EXPLIZIT auf 16 Bit runtergecastet wird...

0x209 * 0x2DC = 0xD1BC.. (binaer: 1101 0001 1011 1100)
Naja in diesem Beispiel ist der Cast sowieso witzlos, da < (2^16)-1

Wie schon geschrieben funktioniert es so:
1
int16_t erg = (int16_t) ((int32_t(a * b)) / ((int32_t)100));

Hier hat man allerdings Nachteile in der Perfomance, da 32 Bit 
Operationen auf einer 16 oder 8 Bit Maschine natuerlich deutlich laenger 
dauern.

Deshalb versuche ich aus Performancegründen zuerst immer die maximal 
moeglichen Wertebereiche abzustecken und danach die Operationen darauf 
auszulegen..

gruss
Gast #1525955
Jupp schrieb:
> Wie schon geschrieben funktioniert es so:
> int16_t erg = (int16_t) ((int32_t(a * b)) / ((int32_t)100));

Nein, so funktioniert es nicht.  bei int32_t(a * b) wird die 
multiplikation is 16 Bit ausgeführt und das Ergebnis in 32 Bit 
gecastet.

Und der cast vor der 100 ist unnötig, das der erste Operand des / schon 
32 Bit ist.

Also:
1
int16_t erg = (int16_t) ( (int32_t)a * b  / 100 );
Persönliche Seite #1525960
Klaus schrieb:
> Also:
>
1
> int16_t erg = (int16_t) ( (int32_t)a * b  / 100 );
2
>

Siehe meine Version, ich habe nur geschickt Klammern eingefügt, damit 
einem sofort klar wird in welcher Reihenfolge die Operatoren 
abgearbeitet werden (Division zu letzt). Dafür nehme ich es in Kauf zwei 
Klammern mehr zu schreiben.
1
int16_t erg = (int16_t)  ( ( (int32_t) a  *  b ) / 100 );

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 16 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.