Ich bin auf Code von jemandem gestoßen, der zu glauben scheint, dass es ein Problem gibt, eine vorzeichenlose Ganzzahl von einer anderen Ganzzahl des gleichen Typs zu subtrahieren, wenn das Ergebnis negativ wäre. Dieser Code wäre also falsch, selbst wenn er auf den meisten Architekturen funktioniert.
unsigned int To, Tf;
To = getcounter();
while (1) {
Tf = getcounter();
if ((Tf-To) >= TIME_LIMIT) {
break;
}
}
Dies ist das einzige vage relevante Zitat aus dem C-Standard, das ich finden konnte.
Eine Berechnung mit vorzeichenlosen Operanden kann niemals überlaufen, da ein Ergebnis, das nicht durch den resultierenden vorzeichenlosen Ganzzahltyp dargestellt werden kann, modulo um die Zahl reduziert wird, die eins größer ist als der größte Wert, der durch den resultierenden Typ dargestellt werden kann.
Ich nehme an, man könnte dieses Zitat so verstehen, dass wenn der richtige Operand größer ist, die Operation so angepasst wird, dass sie im Kontext von Modulo-abgeschnittenen Zahlen sinnvoll ist.
dh
0x0000 - 0x0001 == 0x 1 0000 - 0x0001 == 0xFFFF
im Gegensatz zur Verwendung der implementierungsabhängigen vorzeichenbehafteten Semantik:
0x0000 - 0x0001 == (ohne Vorzeichen) (0 + -1) == (0xFFFF, aber auch 0xFFFE oder 0x8001)
Welche oder welche Interpretation ist richtig? Ist es überhaupt definiert?
Antworten:
Das Ergebnis einer Subtraktion, die eine negative Zahl in einem vorzeichenlosen Typ erzeugt, ist genau definiert:
Wie Sie sehen können,
(unsigned)0 - (unsigned)1
entspricht -1 modulo UINT_MAX + 1 oder mit anderen Worten UINT_MAX.Beachten Sie, dass, obwohl es heißt "Eine Berechnung mit vorzeichenlosen Operanden kann niemals überlaufen", was Sie zu der Annahme führen könnte, dass sie nur für das Überschreiten der Obergrenze gilt, dies als Motivation für den tatsächlich verbindlichen Teil des Satzes dargestellt wird: "a Das Ergebnis, das nicht durch den resultierenden vorzeichenlosen Ganzzahltyp dargestellt werden kann, wird modulo um die Zahl reduziert, die eins größer ist als der größte Wert, der durch den resultierenden Typ dargestellt werden kann. " Dieser Satz ist nicht auf den Überlauf der Obergrenze des Typs beschränkt und gilt gleichermaßen für Werte, die zu niedrig sind, um dargestellt zu werden.
quelle
uint
immer der mathematische Ring der ganzen Zahlen0
durchUINT_MAX
die Operationen der Addition und Multiplikation modulo dargestellt werden sollteUINT_MAX+1
, und nicht daran eines Überlaufs. Es stellt sich jedoch die Frage, warum die Sprache, wenn Ringe ein so grundlegender Datentyp sind, keine allgemeinere Unterstützung für Ringe anderer Größen bietet.Wenn Sie mit vorzeichenlosen Typen arbeiten, findet eine modulare Arithmetik (auch als "Wrap Around" -Verhalten bezeichnet) statt. Um diese modulare Arithmetik zu verstehen , schauen Sie sich einfach diese Uhren an:
9 + 4 = 1 ( 13 mod 12 ), also in die andere Richtung: 1 - 4 = 9 ( -3 mod 12 ). Das gleiche Prinzip wird bei der Arbeit mit vorzeichenlosen Typen angewendet. Wenn der Ergebnistyp ist
unsigned
, findet eine modulare Arithmetik statt.Betrachten Sie nun die folgenden Vorgänge, in denen das Ergebnis als gespeichert wird
unsigned int
:Wenn Sie sicherstellen möchten, dass das Ergebnis vorliegt
signed
, speichern Sie es in einersigned
Variablen oder wandeln Sie es in umsigned
. Wenn Sie den Unterschied zwischen Zahlen ermitteln und sicherstellen möchten, dass die modulare Arithmetik nicht angewendet wird, sollten Sieabs()
die instdlib.h
folgenden Definitionen definierte Funktion in Betracht ziehen.Seien Sie sehr vorsichtig, insbesondere beim Schreiben von Bedingungen, da:
aber
quelle
int d = abs(five - seven);
ist nicht gut. Zuerstfive - seven
wird berechnet: Promotion belässt die Operandentypen alsunsigned int
, das Ergebnis wird modulo berechnet(UINT_MAX+1)
und ausgewertetUINT_MAX-1
. Dann ist dieser Wert der eigentliche Parameterabs
, was eine schlechte Nachricht ist.abs(int)
Verursacht undefiniertes Verhalten beim Übergeben des Arguments, da es nicht im Bereich liegt undabs(long long)
wahrscheinlich den Wert enthalten kann. Undefiniertes Verhalten tritt jedoch auf, wenn der Rückgabewertint
zum Initialisieren gezwungen wirdd
.operator T()
. Die Hinzufügung in den beiden Ausdrücken, die wir diskutieren, erfolgt in Typunsigned int
, basierend auf den Operandentypen. Das Ergebnis der Addition istunsigned int
. Dieses Ergebnis wird dann implizit in den im Kontext erforderlichen Typ konvertiert. Diese Konvertierung schlägt fehl, da der Wert im neuen Typ nicht darstellbar ist.double x = 2/3;
vsdouble y = 2.0/3;
Nun, die erste Interpretation ist richtig. Ihre Argumentation zur "signierten Semantik" in diesem Zusammenhang ist jedoch falsch.
Auch hier ist Ihre erste Interpretation richtig. Arithmetik ohne Vorzeichen folgt den Regeln der Modulo-Arithmetik, dh,
0x0000 - 0x0001
sie wird0xFFFF
für vorzeichenlose 32-Bit-Typen ausgewertet .Die zweite Interpretation (die auf "signierter Semantik" basiert) ist jedoch ebenfalls erforderlich, um das gleiche Ergebnis zu erzielen. Das heißt, selbst wenn Sie
0 - 1
im Bereich des signierten Typs auswerten und-1
als Zwischenergebnis erhalten, ist dies-1
dennoch erforderlich, um zu produzieren0xFFFF
wenn es später in einen nicht signierten Typ konvertiert wird. Selbst wenn eine Plattform eine exotische Darstellung für vorzeichenbehaftete Ganzzahlen verwendet (1er-Komplement, vorzeichenbehaftete Größe), muss diese Plattform beim Konvertieren vorzeichenbehafteter Ganzzahlwerte in vorzeichenlose Ganzzahlregeln Regeln der Modulo-Arithmetik anwenden.Zum Beispiel diese Bewertung
ist nach wie vor produzieren garantiert
UINT_MAX
inc
sogar, wenn die Plattform eine exotische Darstellung für signierte ganze Zahlen verwendet.quelle
Bei vorzeichenlosen Zahlen vom Typ
unsigned int
oder größer wird in Abwesenheit von Typkonvertierungena-b
definiert, dass die vorzeichenlose Zahl erhalten wird, die, wenn sie hinzugefügtb
wird, ergibta
. Die Konvertierung einer negativen Zahl in eine vorzeichenlose Zahl ergibt die Zahl, die beim Hinzufügen zu der vorzeichenumgekehrten ursprünglichen Zahl Null ergibt. Wenn Sie also -5 in vorzeichenlose Zahl umwandeln, erhalten Sie einen Wert, der bei Addition zu 5 Null ergibt.) .Beachten Sie, dass vorzeichenlose Zahlen, die kleiner sind als vor der Subtraktion
unsigned int
zum Typint
heraufgestuft werden können, das Verhalten vona-b
von der Größe von abhängtint
.quelle