Es geht hierbei um die Absicherung vor Überlaufeffekten. Gerade in der Regelungstechnik lassen sich nicht immer Übersteuerungen vermeiden, dann ist Begrenzung und Sättigung nützlich.
Bei der Sättigungsarithmetik von Zweierkomplementzahlen gibt es folgende Fälle zu unterscheiden:
Hierbei geht es um die Einschränkung eines Wertebereiches einer Zahl,
unabhängig von ihrer Herkunft, also Punkt- und Strichrechnung.
Beispielsweise: if (i>10) i=10;
Begrenzung ist in Hochsprachen sehr einfach formulierbar. Siehe Beispiel.
Eine Begrenzung ist auch zweckmäßig bei Bitbreiten-Verringerung; der Übergang zu „Sättigung“ ist in diesem Fall fließend.
Makro-Sammlung (zum Rauskopieren):
PIC | AVR ATmega | 8051
1 Byte in W | 1-Byte-Operanden | 2-Byte-Operanden | 1 Byte in A
| signed X = Maximum aus X und U (untere Grenze) vzb. | if (x<u) x=u;
Vermeiden!
| .macro maxs1 ;X,U cp @0,@1 brge pc+2 mov @0,@1 .endm .macro maxs1k ;X,U cpi @0,@1 brge pc+2 ldi @0,@1 .endm |
---|
0x90 + 0xA0 = 0x130 0x30 ⇒ 0xFF
, hier im Beispiel vzb. 80 + 80 = 160 -96 => 127
Beispiel-Nr. | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|
1. Summand | -15 | 3 | 5 | -5 | -127 | -127 | 127 |
2. Summand | 3 | -3 | 10 | -10 | -1 | -2 | 1 |
Summe (8 bit) | -12 | 0 | 15 | -15 | -128 | 127 | -128 |
Überlauf-Flag V | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
Übertrags-Flag C | 0 | 1 | 0 | 1 | 1 | 1 | 0 |
Negativ-Flag N | 1 | 0 | 0 | 1 | 1 | 0 | 1 |
Korrektur | - | - | - | - | ⇒-127 | ⇒-127 | ⇒127 |
Sättigung ist in Hochsprachen nicht direkt verfügbar! Mancher Leser wird entsprechende MMX- oder SSE2-Befehle kennen.
Makro-Sammlung (zum Rauskopieren):
PIC | AVR ATmega | 8051
1 Byte in W | 1-Byte-Operanden | 2-Byte-Operanden | 1 Byte in A
| Vermeiden! | Ausweich auf vzl. += vzb. .macro sats1 ;A brvc 1f ldi @0,0x7F brmi 1f ldi @0,0x80 1: .endm |
---|
Diese Zahlendarstellung hat folgende Eigenschaften:
Die Tabelle zeigt die Ergebnisse für die beiden (kommutativen) Operanden einer Addition an. Grüne Felder bedeuten, dass die Additionsoperation ausgeführt werden kann und eine Überlaufauswertung (trotzdem) das richtige Ergebnis liefert. Die Addition liefert von sich aus NaN nur beim Aufeinandertreffen von +∞ und –∞.
a b | + | +∞ | NaN | –∞ | – |
---|---|---|---|---|---|
+ | + | +∞ | NaN | –∞ | ± |
+∞ | +∞ | +∞ | NaN | NaN | +∞ |
NaN | NaN | NaN | NaN | NaN | NaN |
–∞ | –∞ | NaN | NaN | –∞ | –∞ |
– | ± | +∞ | NaN | –∞ | – |
Nach der Addition führt ein Überlauf (V-Flag gesetzt) zum Ersetzen durch ±∞, je nach Vorzeichen, entgegengesetzt. Das Ergebnis -32768 wird zu –∞ „angepasst“.
Die Subtraktion ergibt sich durch Addition mit der Negation des zweiten Operanden, fertig.
Bei Multiplikation und Division spielt auch die Null eine Sonderrolle.
a b | 0 | + | +∞ | NaN | –∞ | – |
---|---|---|---|---|---|---|
0 | 0 | 0 | NaN | NaN | NaN | 0 |
+ | 0 | + | +∞ | NaN | –∞ | – |
+∞ | NaN | +∞ | +∞ | NaN | –∞ | –∞ |
NaN | NaN | NaN | NaN | NaN | NaN | NaN |
–∞ | NaN | –∞ | –∞ | NaN | +∞ | +∞ |
– | 0 | – | –∞ | NaN | +∞ | + |
Bei der (nichtkommutativen) Division ist die obere Zeile der Dividend, die linke Spalte der Divisor. Da es — anders als bei IEEE-Gleitkommazahlen — keine positive und negative Null gibt, kann die Division durch Null nur mit NaN „bestraft“ werden.
a b | 0 | + | +∞ | NaN | –∞ | – |
---|---|---|---|---|---|---|
0 | NaN | NaN | NaN | NaN | NaN | NaN |
+ | 0 | + | +∞ | NaN | –∞ | – |
+∞ | 0 | 0 | NaN | NaN | NaN | 0 |
NaN | NaN | NaN | NaN | NaN | NaN | NaN |
–∞ | 0 | 0 | NaN | NaN | NaN | 0 |
– | 0 | – | –∞ | NaN | +∞ | + |
Optimal wäre eine Verarbeitung in Silizium. Das ist derzeit nur bei FPGAs möglich. Die Vor- und Nachbetrachtung der Operanden lässt sich hervorragend parallelisieren und benötigt nur wenig zusätzliche Chipfläche.
Um sich nicht in diese vielen Fallunterscheidungen zu verzetteln und Byte-Operanden zur Bewertung heranzuziehen, werden kurzerhand nur die letzten drei Bits einer jeden Zahl betrachtet, nach folgender Vorverarbeitung:
char al=a&7; // Low-Bits kopieren (∞ und NaN abfangend) if (a) { // wenn nicht Null if ((unsigned)a<0x7FFFu) al=2; // positiv else if ((unsigned)a>0x8001u) al=1; // negativ; auch 6 siehe unten else al^=4; // Bit kippen }
(Das ist nicht dasselbe wie Begrenzung = Bitbreitenreduktion!)
Damit hat al
folgende praktischen Werte:
al | Bedeutung | Bemerkung | |
---|---|---|---|
000 | 0 | Null | |
001 | 1 | negativ | günstig bei Tabellenzugriff |
010 | 2 | positiv | |
011 | 3 | +∞ | |
100 | 4 | NaN | |
101 | 5 | –∞ | |
110 | 6 | negativ | Alternative für programmatische Auswertung |
Die Werte 0, 3, 4 und 5 kommen ohne Berechnungen zu Stande. Damit kann man einen Tabellenzugriff realisieren oder die Auswertung vereinfachen.