mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik integer Verständnis


Autor: elGasto (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
int16_t a = 521;
int16_t b = 732;

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ß

Autor: Mmmh (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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)

Autor: Matthias Lipinsky (lippy)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>wie verhindere ich die Probleme

Zwinge den Compiler, die Berechnung mit einem größeren Datentypen 
durchzuführen:
int16_t erg;
erg = (int16_t)  (  (int32_t) a  *  (int32_t) b  );
erg /= 100;

Autor: Mmmh (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Antwort von Matthias wäre dann Alternative Nr. 3 von mir.

Autor: Sven P. (haku) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: nils (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Es geht nicht darum, dass bei dem Ergebnis die Nachkommastellen
Dein Code kann sowieso nicht mit Kommas umgehen, nur ganzzahlen.

Autor: Mmmh (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Matthias Lipinsky schrieb:
>
> int16_t erg;
> erg = (int16_t)  (  (int32_t) a  *  (int32_t) b  );
> erg /= 100;
> 

Ein mal (int32_t) reicht aber auch ;) Nur ein Operand muss nach int32_t 
gecastet werden, damit die Operation 32bittig ausgeführt wird.

Autor: Matthias Lipinsky (lippy)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>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.


;-)

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Lass ich durchgehen. Aber dein Code funktioniert trotzdem nicht. Du 
musst die Division durch 100 auch in 32 Bit ausführen :-) Bisher wird 
einfach nur in 32 Bit multipliziert und dann wieder auf 16 Bit gestutzt.

Richtig:
int16_t erg;
erg = (int16_t)  ( ( (int32_t) a  *  b ) / 100 );

Autor: Mmmh (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>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.

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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
erg /= 100
enthält keinen Cast nach 32 Bit, erg ist aber 16 Bit. Ergo wird die 
Division in 16 Bit ausgeführt.

Autor: Mmmh (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hast recht. Ich habe übersehen, das die Division jetzt in einem eigenen 
Ausdruck steht.

Autor: Rumkugel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Fängt das Problem nicht eigentlich schon eine Zeile früher an?
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.

Autor: Jupp (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
A:
erg = (int16_t)  (  (int32_t) a  *  (int32_t) b  );

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

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:
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

Autor: Klaus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
int16_t erg = (int16_t) ( (int32_t)a * b  / 100 );

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Klaus schrieb:
> Also:
>
> int16_t erg = (int16_t) ( (int32_t)a * b  / 100 );
> 

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.
int16_t erg = (int16_t)  ( ( (int32_t) a  *  b ) / 100 );

Autor: Klaus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Japp, deine Version is korrekt. Ich wollt nur auf den Fehler in Jupps 
Beitrag hinweisen.

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Klaus schrieb:
> Japp, deine Version is korrekt. Ich wollt nur auf den Fehler in Jupps
> Beitrag hinweisen.

Dann noch einen schönen Advent! :-)

Autor: Klaus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja, dir natürlich auch einen schönen 4. Advent :D

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.