Forum: Compiler & IDEs Ergebnis einer Rechenoperation auf den Wertebereich begrenzen


von Kottan (Gast)


Lesenswert?

Hallo!

Gibt es eine einfache Möglichkeit das Ergebnis einer Rechenoperation so 
zu begrenzen das es den Wertebereich der Variable nich überschreiten 
kann?
Also z.B. bei
1
 
2
uint8_t Foo=10  
3
Foo -= 100
sollte das ergbnis 0 sein
oder bei
1
Foo *= 100
das Ergbnis 255.

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

Nur mit zusätzlichem Code, den man bevorzugt in (static inline) 
Hilfsfunktionen unterbringt.

Gruß
Marcus
http://www.doulos.com/arm/

von (prx) A. K. (prx)


Lesenswert?

Prozessoren mit Befehlssatzerweiterung für DSP oder Multimedia 
unterstützen manche Formen saturierter Arithmetik. Üblicherweise gibt es 
dann Include-Files für Pseudofunktionen, mit denen man dies nutzen kann.

Portabel ist das natürlich nicht.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Das heisst Saturierung was sich übersetzen lässt mit Sättigung und 
ist keine Eigenschaft des Ergebnisses oder eines Wertes, sondern eines 
Operators.

Mansche Maschinen kennen Instruktionen für saturierte Operationen wie 
Addition, Subtraktion, Betrag, Shift. Oft dann in den 
Geschmacksrichtungen signed und unsigned.

Manche GCC-Versionen stellen Builtin-Funktionen dafür zur Verfügung, 
z.b. für ARM Neon.

Ansonsten muss man die "normale" Operation über einem weiteren 
Werteberech ausführen in dem kein Über/Unterlauf auftreten kann und dann 
per Vergleich bzw. Minimum/Maximum auf die gewünschten Bereich 
begrenzen.

Johann

von Karl H. (kbuchegg)


Lesenswert?

Johann L. schrieb:

> Ansonsten muss man die "normale" Operation über einem weiteren
> Werteberech ausführen in dem kein Über/Unterlauf auftreten kann und dann
> per Vergleich bzw. Minimum/Maximum auf die gewünschten Bereich
> begrenzen.

Wenn man eigene Funktionen schreiben muss, sollte man auch darüber 
nachdenken, ob es möglich ist vor der Operation schon vorherzusagen, ob 
das Ergebnis den Bereich verlassen wird.

Im obigen Beispiel ist das sehr leicht möglich


  uint8_t Foo=10
  if( Foo > 100 )
    Foo -= 100
  else
    Foo = 0;

Bei komplizierteren Ausdrücken, kann das natürlich entsprechend 
komplexer werden.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Karl heinz Buchegger schrieb:
> Johann L. schrieb:
>
>> Ansonsten muss man die "normale" Operation über einem weiteren
>> Werteberech ausführen in dem kein Über/Unterlauf auftreten kann und dann
>> per Vergleich bzw. Minimum/Maximum auf die gewünschten Bereich
>> begrenzen.
>
> Wenn man eigene Funktionen schreiben muss, sollte man auch darüber
> nachdenken, ob es möglich ist vor der Operation schon vorherzusagen, ob
> das Ergebnis den Bereich verlassen wird.
>
> Im obigen Beispiel ist das sehr leicht möglich
>
>
>   uint8_t Foo=10
>   if( Foo > 100 )
>     Foo -= 100
>   else
>     Foo = 0;
>

Das tut es glaub nicht.
1
uint8_t a, b;
2
uint8_t ab = a+b;
3
4
if (ab < a)
5
  ab = 255;

und signed:
1
int8_t a, b;
2
int8_t ab = a+b;
3
4
if (b >= 0)
5
{
6
  if  (ab < a)
7
     ab = 127;
8
}
9
else
10
{
11
  if  (ab > a)
12
     ab = -128;
13
}

von Kottan (Gast)


Lesenswert?

Danke für die Antworten!
Bis jetzt hab ich es auch immer so gelöst das ich eine variable mit 
größerem Wertebereich genommen hab und dann mit 2 if's begrenzt hab. 
Dachte nur vielleicht gibt es eine einfachere Platz/Zeitsparende 
Möglichkeit.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Je nach Maschine/Compiler kann man zB das Carry-Flag dazu ausnutzen und 
muss nicht auf einen größeren Typen ausweichen.

Für AVR+GCC geht zum Beispiel
1
#include <stdint.h>
2
3
// unsigned saturated addition, 8 bit
4
static inline uint8_t usadd8 (uint8_t a, uint8_t b)
5
{
6
    asm  ("add %0, %1"  "\n\t"
7
          "brcc 0f"     "\n\t"
8
          "ldi %0, 255" "\n\t"
9
          "0:"
10
          : "+d" (a)
11
          : "r" (b));
12
          
13
    return a;
14
}
15
16
uint8_t add42 (uint8_t a, uint8_t b)
17
{
18
    return usadd8 (usadd8 (a, 42), b);
19
}

Der Code ist dann
1
add42:
2
  ldi r25,lo8(42)   ;  tmp46
3
  add r24, r25   ;  a, tmp46
4
  brcc 0f
5
  ldi r24, 255   ;  a
6
  0:
7
  add r24, r22   ;  a, b
8
  brcc 0f
9
  ldi r24, 255   ;  a
10
  0:
11
  ret

Johann

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.