Frage zu Wertebereichen bzw. Verkuerzung von Ergebnissen

04/01/2009 - 16:05 von Felix Opatz | Report spam
Hallo Leute,

ich habe gerade eine halbe Stunde lang nach einem Fehler gesucht, und
bin jetzt über die Ursache ziemlich verwundert.

Folgenden Quellcode habe ich:

uint16_t result;
result = ADC * 2560 / 10230;

wobei ADC hier als 16 Bit R-Value wirkt, und in Wirklichkeit komischer
Zauber ist. Es tut zwar IMHO nichts zur Sache, aber der Vollstàndigkeit
halber:

#define ADC _SFR_IO16(0x04)
#define _SFR_IO16(io_addr) _MMIO_WORD((io_addr) + 0x20)
#define _MMIO_WORD(mem_addr) (*(volatile uint16_t *)(mem_addr))

(Es sei noch angemerkt, daß ich eigentlich durch 1023 teilen muß, das
ist nur für die Fehlersuche 10230 geworden, weil ich auf einem Display
bloß 3 Stellen zur Ausgabe bringen kann; beide Konstanten um 10 kürzen
bringt mich also nicht weiter und ist auch nicht Bestandteil der Frage.)

Ich erhalte für einen Wert von 768 für ADC das Ergebnis 0, hàtte aber
192 erwartet. Was passiert ist mir auch klar, die Multiplikation ergibt
einen Wert der zu groß für eine 16-Bit-Variable ist. Nach §6.3.1.3,2
wird solange 2^16 abgezogen bis es reinpasst, was bei mir zu dem Wert 0
führt. 0 / 10230 ist dann auch 0. Wenn ich ADC vor Gebrauch auf uint32_t
caste kommt auch das gewünschte Ergebnis von 192 raus.

Meine Verwunderung entsteht nun aber dadurch, daß ich diese implizite
Zuweisung in einen uint16_t nicht sehe. Wenn ich einen uint16_t mit
etwas multipliziere, sodaß das Ergebnis nicht mehr in einen uint16_t
passt, hàtte ich jetzt erwartet, daß ein ausreichend großer Datentyp
gewàhlt wird, und eine Verkürzung erst mit der Zuweisung passiert (oder
einem expliziten Cast).

result = ADC * 2560UL / 10230;

führt übrigens ebenfalls zum korrekten Ergebnis. Was mich nun doch sehr
stört ist die Tatsache, daß ich wohl davon ausgehen muß, daß bei
uint16_t * uint16_t das Ergebnis auch ohne Zuweisung an einen uint16_t
verkrüppelt wird.

Für die Plattform gilt übrigens (und ich habe so den Verdacht, daß dies
das Entscheidende ist): sizeof(int) = 2, sizeof(long) = 4.

Ich schau gerade schon nach §6.3.1.1,2. Muß man das so verstehen, daß
unsigned int das "Ende" der Integer-Promotions ist, d.h. nicht
automatisch auf unsigned long promoted wird? Etwas in unsigned long zu
konvertieren wirkt aber als Schutz davor, daß es wieder unsigned int
wird? Ist das schon die Erklàrung für meine Beobachtung?

Gruß,
Felix
 

Lesen sie die antworten

#1 Stefan Reuther
04/01/2009 - 16:34 | Warnen spam
Felix Opatz wrote:
uint16_t result;
result = ADC * 2560 / 10230;


[...]
Ich schau gerade schon nach §6.3.1.1,2. Muß man das so verstehen, daß
unsigned int das "Ende" der Integer-Promotions ist, d.h. nicht
automatisch auf unsigned long promoted wird? Etwas in unsigned long zu
konvertieren wirkt aber als Schutz davor, daß es wieder unsigned int
wird? Ist das schon die Erklàrung für meine Beobachtung?



Genau so ist das. Vereinfacht: ein Ausdruck wird immer in 'int'
berechnet, es sei denn, einer der Operanden hat einen größeren Typ.

Ob das (Zwischen)Ergebnis in 'int' passt ist dabei egal. Würde der
Compiler immer damit rechnen, dass das Ergebnis nicht passt (was bei
quasi jeder nichttrivialen Rechenoperation der Fall ist) und entsprechend
auf 'long' wechseln, würde grundsàtzlich langsame umstàndliche
Multiwort-Arithmetik verwendet. Das willst du auf einem 8-bitter nicht
wirklich.


Stefan

Ähnliche fragen