it-swarm.com.de

Wie subtrahieren Sie zwei unsignierte Ints mit Umlauf oder Überlauf

Es gibt zwei vorzeichenlose Ints (x und y), die abgezogen werden müssen. x ist immer größer als y. Sowohl x als auch y können jedoch umlaufen. Wenn sie zum Beispiel beide Bytes waren, kommt nach 0xff 0x00. Der Problemfall ist, wenn x sich umläuft und y nicht. Jetzt scheint x kleiner als y zu sein. Glücklicherweise wird x nicht zweimal umlaufen (nur einmal ist garantiert). Unter der Annahme, dass Bytes vorhanden ist, ist x umbrochen und ist jetzt 0x2, während y nicht 0xFE ist und ist. Die richtige Antwort von x - y soll 0x4 sein. 

Könnte sein, 

( x > y) ? (x-y) : (x+0xff-y);

Aber ich denke, es gibt einen anderen Weg, etwas, das ein 2s-Kompliment beinhaltet?, Und in diesem eingebetteten System sind x und y die größten vorzeichenlosen Int-Typen. Daher ist das Hinzufügen von 0xff nicht möglich

Wie schreibe ich die Aussage am besten (Zielsprache ist C)?

19
mgag

Angenommen, zwei unsigned Ganzzahlen:

  • Wenn Sie wissen, dass einer "größer" als der andere sein soll, ziehen Sie einfach ab. Es wird funktionieren, vorausgesetzt, Sie haben sich nicht mehr als einmal umwickelt (natürlich können Sie dies nicht sagen).
  • Wenn Sie nicht wissen, dass einer größer ist als der andere, subtrahieren Sie das Ergebnis und wandeln Sie es in ein vorzeichenbehaftetes Int der gleichen Breite. Es funktioniert, vorausgesetzt, der Unterschied zwischen den beiden liegt im Bereich des vorzeichenbehafteten int (wenn nicht, können Sie das nicht sagen).

Zur Verdeutlichung: Das vom Originalposter beschriebene Szenario scheint verwirrend zu sein, ist aber typisch für monoton steigende Zähler mit fester Breite, z. B. Hardware-Tick-Zähler oder Sequenznummern in Protokollen. Der Zähler geht (z. B. für 8 Bits) 0xfc, 0xfd, 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03 usw., und Sie wissen, dass x von den beiden Werten x und y später kommt. Wenn x == 0x02 und y == 0xfe ist, ergibt die Berechnung x-y (als 8-Bit-Ergebnis) die richtige Antwort von 4, vorausgesetzt, dass die Subtraktion von zwei n - Bit-Werten Modulo 2 umschließtn - welche C99 für die Subtraktion von vorzeichenlosen Werten garantiert. (Hinweis: Der C-Standard garantiert nicht garantiert dieses Verhalten für die Subtraktion von mit Vorzeichen .)

28

Hier ist ein wenig genauer, warum es „nur funktioniert“, wenn Sie „kleiner“ von „größer“ subtrahieren.

Ein paar Dinge, die in diese Sache einfließen…
1. In der Hardware verwendet die Subtraktion den Zusatz: Der entsprechende Operand wird einfach negiert, bevor er hinzugefügt wird.
2. Im Zweierkomplement (das fast alles verwendet) wird eine ganze Zahl negiert, indem alle Bits invertiert werden und dann 1 addiert wird.

Hardware macht dies effizienter als es sich aus der obigen Beschreibung anhört. Dies ist jedoch der grundlegende Algorithmus für die Subtraktion (auch wenn die Werte nicht vorzeichenbehaftet sind).

Stellen wir uns also 2 - 250 mit vorzeichenlosen 8-Bit-Ganzzahlen vor. In binär haben wir

  0 0 0 0 0 0 1 0  
- 1 1 1 1 1 0 1 0

Wir negieren den abgezogenen Operanden und addieren ihn dann. Es sei daran erinnert, dass wir zum Negieren alle Bits invertieren und dann 1 hinzufügen. Nach dem Invertieren der Bits des zweiten Operanden haben wir 

0 0 0 0 0 1 0 1  

Dann nach dem Hinzufügen von 1 haben wir 

0 0 0 0 0 1 1 0  

Jetzt führen wir die Addition durch ...

  0 0 0 0 0 0 1 0   
+ 0 0 0 0 0 1 1 0

= 0 0 0 0 1 0 0 0 = 8, which is the result we wanted from 2 - 250
20
Purdude

Vielleicht verstehe ich nicht, aber was ist los mit:

unsigned r = x - y;

13
GManNickG

Die Frage ist, wie gesagt, verwirrend. Sie sagten, dass Sie vorzeichenlose Werte subtrahieren. Wenn x immer größer als y ist, wie Sie gesagt haben, kann x - y möglicherweise nicht umlaufen oder überlaufen. Also machst du einfach x - y (wenn es das ist, was du brauchst) und das ist es.

3
AnT

Dies ist eine effiziente Methode zum Ermitteln des freien Speicherplatzes in einem Ringspeicher oder zur Steuerung des Gleitfensters. Verwenden Sie unsigned ints für head und tail - erhöhen Sie diese und lassen Sie sie umlaufen! Pufferlänge hat eine Potenz von 2 sein.

free = ((head - tail) & size_mask), wobei size_mask 2 ^ n-1 der Puffer- oder Fenstergröße ist.

2
C guy

Das Problem sollte wie folgt angegeben werden:

Nehmen wir an, die Position (Winkel) der zwei Zeiger a und b einer Uhr wird durch einen uint8_t gegeben. Der gesamte Umfang ist in die 256 Werte eines uint8_t unterteilt. Wie kann der kleinere Abstand zwischen den beiden Zeigern effizient berechnet werden?

Eine Lösung ist:

uint8_t smaller_distance = abs( (int8_t)( a - b ) );

Ich vermute, dass es nichts effizienter gibt, da sonst etwas effizienter wäre als abs ().

1
hpc64

Um die ohnehin richtige Antwort in den Code zu packen:

Wenn Sie wissen, dass x der kleinere Wert ist, funktioniert die folgende Berechnung einfach:

int main()
{
    uint8_t x = 0xff;
    uint8_t y = x + 20;
    uint8_t res = y - x;
    printf("Expect 20: %d\n", res); // res is 20

    return 0;
}

Wenn Sie nicht wissen, welche kleiner ist:

int main()
{
    uint8_t x = 0xff;
    uint8_t y = x + 20;
    int8_t res1 = (int8_t)x - y;
    int8_t res2 = (int8_t)y - x;
    printf("Expect -20 and 20: %d and %d\n", res1, res2);

    return 0;
}

Der Unterschied muss in diesem Fall innerhalb des Bereichs von uint8_t liegen.

Das Code-Experiment half mir, die Lösung besser zu verstehen.

0
Tarion

Um allen anderen zu antworten, wenn Sie nur die beiden subtrahieren und das Ergebnis als nicht signiert interpretieren, werden Sie in Ordnung sein. 

Es sei denn, Sie haben ein explizites Gegenbeispiel.

Ihr Beispiel für x = 0x2, y= 0x14 würde nicht zu 0x4 führen, sondern 0xEE, es sei denn, Sie haben weitere Einschränkungen für die Mathematik, die nicht angegeben sind.

0
MSN