www.mikrocontroller.net

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


Autor: Kottan (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
 
uint8_t Foo=10  
Foo -= 100
sollte das ergbnis 0 sein
oder bei
Foo *= 100
das Ergbnis 255.

Autor: Marcus Harnisch (mharnisch) Benutzerseite
Datum:

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

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

Autor: A. K. (prx)
Datum:

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

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

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

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.
uint8_t a, b;
uint8_t ab = a+b;

if (ab < a)
  ab = 255;

und signed:
int8_t a, b;
int8_t ab = a+b;

if (b >= 0)
{
  if  (ab < a)
     ab = 127;
}
else
{
  if  (ab > a)
     ab = -128;
}

Autor: Kottan (Gast)
Datum:

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

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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
#include <stdint.h>

// unsigned saturated addition, 8 bit
static inline uint8_t usadd8 (uint8_t a, uint8_t b)
{
    asm  ("add %0, %1"  "\n\t"
          "brcc 0f"     "\n\t"
          "ldi %0, 255" "\n\t"
          "0:"
          : "+d" (a)
          : "r" (b));
          
    return a;
}

uint8_t add42 (uint8_t a, uint8_t b)
{
    return usadd8 (usadd8 (a, 42), b);
}

Der Code ist dann
add42:
  ldi r25,lo8(42)   ;  tmp46
  add r24, r25   ;  a, tmp46
  brcc 0f
  ldi r24, 255   ;  a
  0:
  add r24, r22   ;  a, b
  brcc 0f
  ldi r24, 255   ;  a
  0:
  ret

Johann

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.