integer Verständnis

Gast #1525612
Lesenswert?

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
Lesenswert?

>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
Lesenswert?

>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
Lesenswert?

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
Lesenswert?

>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.
Persönliche Seite #1525731
Lesenswert?

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
Lesenswert?

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
Lesenswert?

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
Lesenswert?

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
Lesenswert?

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

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren