Forum: Compiler & IDEs Sättigende Integer-Addition


von Walter T. (nicolas)


Lesenswert?

Hallo zusammen,

ich habe gerade eine (einfache) Funktion geschrieben, um die Addition 
von Werten auf einen Bereich zu begrenzen:
1
/* Addition mit Begrenzung auf waehlbaren Bereich
2
 * val:  Summand 1
3
 * incr: Summand 2
4
 * min:  Untere Wertegrenze
5
 * max:  Obere Wertegrenze */
6
uint8_t limadd_uint8(uint8_t val,int8_t incr,uint8_t min,uint8_t max)
7
{
8
    int16_t temp;
9
    
10
    temp = val+incr;
11
12
    // Fehlerhafte max/min abfangen
13
    if (temp > (int16_t) UINT8_MAX) {
14
        temp = UINT8_MAX;
15
    }
16
    else if (temp < 0) {
17
        temp = 0;
18
    }
19
    
20
    if (temp > max) {
21
        temp = max;
22
    }
23
    else if (temp < min) {
24
        temp = min;
25
    }
26
    
27
    return (uint8_t) temp;
28
}

Nun frage ich mich: soetwas ist doch vermutlich eine ziemlich oft 
benötigte Funktion (implizit kommt sie in meinem Quelltext mit 
unterschiedlichen Datentypen zweistellig vor und wird nur gerade in eine 
einzelne Funktion pro Datentyp gebracht). Gibt es soetwas nicht schon in 
der C-Standard-Library?

Viele Grüße
W.T.

von (prx) A. K. (prx)


Lesenswert?

Walter Tarpan schrieb:
> Nun frage ich mich: soetwas ist doch vermutlich eine ziemlich oft
> benötigte Funktion

In DSP-Code, Audio- und Videoverarbeitung kommt saturierende Arithmetik 
oft vor, in den meisten anderen Bereichen über die Häufigkeit 
ausgeführter Befehle betrachtet jedoch selten. Oft gibt es bei in diesen 
Bereichen eingesetzten Prozessoren SIMD-Befehle (x86 ab MMX) für 
saturierende Addition/Subtraktion und prozessorspezifische Libraries um 
diese zu nutzen.

von Walter T. (nicolas)


Lesenswert?

Guten Morgen,
an DSPs habe ich noch nicht einmal gedacht. Hier sind es gerade so 
einfache Sachen wie "erlaubter Wertebereich in xy-Einstellungen".

Mir geht es aber auch gar nicht mal darum, das möglichst effizient oder 
clever umzusetzen - die Funktionen tun das, was sie sollen und sind an 
keiner Stelle, wo die Geschwindigkeit wichtig ist.

Mir geht es darum, ob ich gerade eine Standard-Library-Funktion 
nachimplementiere (wobei ich mich schon ein paarmal erwischt habe, wenn 
ich Wochen später zufällig auf den passenden Header gestoßen bin).

von Hans (Gast)


Lesenswert?

Aktuelle GCC-Versionen haben eigene Datentypen für 
Fixed-Point-Arithmetik mit Saturierung:
http://gcc.gnu.org/onlinedocs/gcc-4.9.0/gcc/Fixed-Point.html#Fixed-Point

von Uwe (de0508)


Lesenswert?

Hallo,

und wie behandelst Du einen Überlauf, wenn die Addition überhaupt nicht 
mit 16Bit ausgeführt wird?
1
temp = val+incr;

von (prx) A. K. (prx)


Lesenswert?

Uwe S. schrieb:
> und wie behandelst Du einen Überlauf, wenn die Addition überhaupt nicht
> mit 16Bit ausgeführt wird?

Sie wird hier mit mindestens 16 Bits ausgeführt.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Sind da nicht 2 Vergleiche bzw. MIN/MAX-Berechnungen überflüssig?

Etwa ist immer max <= UINT8_MAX und daher

   MIN (temp, UINT8_MAX, max) = MIN (temp, max)

und analog wegen min >= 0

   MAX (temp, 0, min) = MAX (temp, min)

d.h. deine Funktion macht nichts anderes als
1
uint8_t limadd_uint8 (uint8_t val, int8_t incr, uint8_t min, uint8_t max)
2
{
3
    int16_t temp = val + incr;
4
5
    if (temp > max)
6
        temp = max;
7
8
    if (temp < min)
9
        temp = min;
10
    
11
    return (uint8_t) temp;
12
}

von Walter T. (nicolas)


Angehängte Dateien:

Lesenswert?

Johann L. schrieb:
> Sind da nicht 2 Vergleiche bzw. MIN/MAX-Berechnungen überflüssig?

Du hast Recht. Und der Compiler ist in dieser Frage auch deutlich 
mutiger als ich vorgegangen. Ich bin bei Vergleichen Int/unsigned int 
vielleicht etwas zu mißtrauisch.

von Gaestchen (Gast)


Lesenswert?

Johann L. schrieb:
> d.h. deine Funktion macht nichts anderes als

Da wäre ich mir an Deiner Stelle nicht so wirklich sicher.  Übergib mal 
( 0, -2,-2, 100)

von Walter Tarpan (Gast)


Lesenswert?

Gaestchen schrieb:
> Da wäre ich mir an Deiner Stelle nicht so wirklich sicher.  Übergib mal
> ( 0, -2,-2, 100)

Naja, also min > max würde ich allerdings schon als "grobe Gemeinheit" 
bezeichnen.

von Kaj (Gast)


Lesenswert?

Gaestchen schrieb:
> Johann L. schrieb:
>> d.h. deine Funktion macht nichts anderes als
>
> Da wäre ich mir an Deiner Stelle nicht so wirklich sicher.  Übergib mal
> ( 0, -2,-2, 100)
Wer unzulaessige Werte an eine Funktion uebergibt, der muss sich nicht 
wundern wenn die Funktion nicht das macht, was man erwartet. Ganz 
einfach.
Und wer bei einem Argument, das mit "uint..." deklariert ist, eine -2 
uebergibt, dem ist eh nicht zu helfen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Walter Tarpan schrieb:
> Gaestchen schrieb:
>> Da wäre ich mir an Deiner Stelle nicht so wirklich sicher.  Übergib mal
>> ( 0, -2,-2, 100)
>
> Naja, also min > max würde ich allerdings schon als "grobe Gemeinheit"
> bezeichnen.

Zudem ist die Frage, was wäre dann eine sinnvolle Ausgabe wenn gefordert 
werd, daß die Ausgabe min <= wert <= max erfüllt?

Wenn min > max dann ist wegen

   max < min <= wert <= max < min

Das bedeutet dann max < max und min < min, was schwerlich zu erfüllen 
ist...

von Andreas S. (Firma: Schweigstill IT) (schweigstill) Benutzerseite


Lesenswert?

Hans schrieb:
> http://gcc.gnu.org/onlinedocs/gcc-4.9.0/gcc/Fixed-Point.html#Fixed-Point

Und zwar nicht erst seit GCC 4.9, sondern schon 4.6. Jedoch muss der 
Compiler (und die C-Bibliothek?) mit der Option "--enable-fixed-point" 
übersetzt worden sein:

http://stackoverflow.com/questions/11005162/how-to-make-use-of-the-gcc-fixed-point-types-extension-on-arm-cortex-m

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Andreas Schweigstill schrieb:
> Hans schrieb:
>> http://gcc.gnu.org/onlinedocs/gcc-4.9.0/gcc/Fixed-Point.html#Fixed-Point
>
> Und zwar nicht erst seit GCC 4.9, sondern schon 4.6. Jedoch muss der
> Compiler (und die C-Bibliothek?) mit der Option "--enable-fixed-point"
> übersetzt worden sein:

Nicht unbedingt, wenn der entsprechende Support im GCC Backend aktiviert 
ist, dann ist --enable-fixed-point nicht notwendig.  Dies ist z.B. der 
Fall ab avr-gcc  4.8

Und erst ab avr-gcc 4.8 sind einige Typen optimiert untestützt und nicht 
nur durch zwar funktionierende, aber extrem ineffiziente 
libgcc-Implementierungen verfügbar.

Um eine saturierende 16-Bit short Addition zu erhalten, kann man recht 
umständlich:
 
1
#include <stdfix.h>
2
3
short satadd (short a, short b)
4
{
5
    sat fract fa = rbits (a);
6
    sat fract fb = rbits (b);
7
8
    return bitsr (fa + fb);
9
    
10
}
 
Was dann ergibt:
 
1
satadd:
2
  add r24,r22
3
  adc r25,r23
4
  brvc 0f
5
  ldi r25,0x80
6
  cp r23,r25
7
  sbc r24,r24
8
  sbci r25,0
9
  0:
10
  ret

Die Saturierung ist allerding immer auf die Bitbreite des Typs; so 
allgemein wie im OP mit frei wählbaren min und max ist sie nicht. 
Allerdings kann sie dazu verwendet werden, in der gleichen Bitbreite zu 
bleiben und nicht den nächst größeren Typ (falls es so einen gibt) 
verwenden zu müssen.

Im OP liegt jedoch ein besonderer Fall von unsigned+signed vor, der 
eigentlich nicht kommutativ ist:

Bei

   (unsigned char) 255 + (signed char) -1

will man als Ergebnis (unsigned char) 254 haben, während es bei

   (signed) -1 + (unsigned) 255

an der (signed char) Obergrenze anschlägt und das Ergebnis (signed char) 
127 sein soll.

Die Fixed-Point Typen des GCC hingegen haben die gleichen Promotions von 
unsigned (als Qualifier) wie int-Typen auch.

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.