Forum: Mikrocontroller und Digitale Elektronik Berechnung von uint8_t ohne Überlauf


von Stefan S. (sschultewolter)


Lesenswert?

Hallo,

ich hätte eine kurze Frage
1
    rgb[0].r = (uint16_t)((val * color->r) / 255); 
2
    rgb[0].g =  (uint16_t)((val * color->g) / 255); 
3
    rgb[0].b =  (uint16_t)((val * color->b) / 255);

Bei dieser Berechnung kann der Wert zwischenzeitglich größer 255 (1 
Byte) werden. Ist das uint16_t so richtig angewendet?
rgb[0].r, g, b sind jeweils uint8_t

Desweiteren wäre es sinnvoller, das ganze anstatt mit "/ 255" besser mit 
>> 7 zu machen?

von holger (Gast)


Lesenswert?

>Bei dieser Berechnung kann der Wert zwischenzeitglich größer 255 (1
>Byte) werden. Ist das uint16_t so richtig angewendet?

Nein.

>Desweiteren wäre es sinnvoller, das ganze anstatt mit "/ 255" besser mit
>>> 7 zu machen?

>>7 bedeutet /128. Ob das besser ist? Und wieso teilst du nicht durch 256?
Nur dann könnte man schieben.

von Stefan S. (sschultewolter)


Lesenswert?

Wie ist das nein zu deuten? Das der Wert im Endeffekt nicht größer als 
255 wird, ist klar. Das liegt aber auch nur daran dass zum einen es das 
Endergebnis nicht zulässt, zum anderen aber auch, da es zum Überlauf 
kommt.

Wo muss ich ggf. das (uint16_t) setzen?
_r = ((uint16_t)val * r) / 255

Durch 256 teilen macht eigentlich doch wenig Sinn, dann gibt es den 
vollen Ausschlag (255) nicht mehr. Spielt hier auch eher weniger die 
Rolle, der Unterschied ist um 1 spielt keine Rolle. Ist das Shiften hier 
deutlich effizienter? Im Einsatz ist ein Atiny841

von B. S. (bestucki)


Lesenswert?

Stefan S. schrieb:
> Ist das uint16_t so richtig angewendet?

In deinem ersten Beispiel hast du das Ergebnis der Multiplikation 
gecastet, nicht die Operanden der Multiplikation, so wie in deinem 
zweiten Beispiel.

Der Standard verlangt, dass in mindestens 16Bit (Mindestgrösse int) 
gerechnet wird (in deinem Fall in signed). Wenn du in unsigned rechnen 
willst, musst du einen der Operanden nach unsigned (hier uint16_t) 
casten. Ist ein int grösser, z.B. 32Bit, spielt es in deinem Falle keine 
Rolle, ob du in signed oder unsigned rechnest. Ich würde den Cast 
jedenfalls drinlassen, da er aussagt, dass du mindestens in 16Bit 
unsigned rechnen willst.

Edit: Ich habe die Annahme gemacht, dass deine beiden Operatoren vom Typ 
uint8_t sind.

: Bearbeitet durch User
von Noch einer (Gast)


Lesenswert?

Genau genommen, kommt es auf den Compiler an.

Nach C Standard muss der Compiler zuerst nach Integer konvertieren und 
dann alles mit Integer rechnen - der Cast wäre überflüssig.

Compiler für Mikrocontroller rechnen normalerweise entgegen dem 
C-Standard mit 8Bit Zahlen und 8Bit Ergebnis. Du musst als allererstes 
einen der 8Bit Werte auf 16Bit casten. Dann rechnet der Compiler mit 
16Bit = (16Bit * 16Bit) / 16Bit.

rgb[0].r = ((((uint16_t)val) * color->r) / 255);

(Eigentlich recht unsinnig. Viele Mikrocontroller haben Hardware, die 
16Bit = 8Bit * 8Bit sehr schnell berechnen kann.)

Ob du /256 oder >>8 schreibst? Eigentlich egal - die Optimizer sind so 
gut, machen sowieso das selbe daraus. Wenn du eine 100% sichere Antwort 
haben willst - vom Compiler ein Assembler-Listing ausgeben lassen.

von B. S. (bestucki)


Lesenswert?

Stefan S. schrieb:
> Ist das Shiften hier
> deutlich effizienter? Im Einsatz ist ein Atiny841

Wenn du nach unsigned castest, wird der Compiler die Division 
automatisch durch einen Shift ersetzen (nur bei 2er-Potenzen). Wir 
hatten ein ähnliches Thema letztens hier im Forum (signed vs. unsigned 
beim Shiften):
Beitrag "Re: Einstieg in C - komische Fragen"

Edit: Habs gerade durch den Compiler gejagt, er nimmt nach der 
Multiplikation einfach das höherwertige Byte und verwirft den Rest.
1
A = ((uint16_t)B * C) / 256;

: Bearbeitet durch User
von Peter II (Gast)


Lesenswert?

Noch einer schrieb:
> Compiler für Mikrocontroller rechnen normalerweise entgegen dem
> C-Standard mit 8Bit Zahlen und 8Bit Ergebnis.

von welchen Compiler redest du?

der GCC rechnet int (16bit). (wenn man ihn nicht extra zwingt)

von Oliver S. (oliverso)


Lesenswert?

Noch einer schrieb:
> Compiler für Mikrocontroller rechnen normalerweise entgegen dem
> C-Standard mit 8Bit Zahlen und 8Bit Ergebnis.

Wenn die das unaufgefordert machen, sind es keine C-Compiler. Das ist 
dann irgend etwas C-ähnliches, aber kein C.

avr-gcc ist ein C-Compiler.

Oliver

von (prx) A. K. (prx)


Lesenswert?

Noch einer schrieb:
> Compiler für Mikrocontroller rechnen normalerweise entgegen dem
> C-Standard mit 8Bit Zahlen und 8Bit Ergebnis.

Mal so mal so, ausserdem sind allenfalls 8-Bit Mikrocontroller davon 
betroffen. Es sollte dann auch eine Option geben, die konformes 
Verhalten zur Folge hat (oder andersrum).

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.