C: Unäres Minus-Operatorverhalten mit vorzeichenlosen Operanden

Lesezeit: 5 Minuten

Benutzeravatar von Alexey Frunze
Alexej Frunze

Ich kann anscheinend die relevanten Teile im C-Standard nicht finden, die das Verhalten des unären Minusoperators mit vorzeichenlosen Operanden vollständig definieren.

Der C++-Standard von 2003 (ja, C++, ertragen Sie mich für ein paar Zeilen) sagt in 5.3.1c7: The negative of an unsigned quantity is computed by subtracting its value from 2^n, where n is the number of bits in the promoted operand.

Der C-Standard von 1999 enthält jedoch keine solche explizite Aussage und definiert das Unär-Verhalten weder in 6.5.3.3c1,3 noch in 6.5c4 klar. Bei letzterem heißt es Some operators (the unary operator ~, and the binary operators <<, >>, &, ^, and |, ...) ... return values that depend on the internal representations of integers, and have implementation-defined and undefined aspects for signed types.)das das einstellige Minus ausschließt und die Dinge vage zu bleiben scheinen.

Diese frühere Frage bezieht sich auf das K&R ANSI C-Buch, Abschnitt A.7.4.5, in dem es heißt The negative of an unsigned quantity is computed by subtracting the promoted value from the largest value of the promoted type and adding one.

Was wäre der C-Standard von 1999, der dem obigen Zitat aus dem Buch entspricht?

6.2.5c9 sagt: A computation involving unsigned operands can never overflow, because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting type.

Ist es das? Oder fehlt mir noch etwas?

  • Die Ergebnisse der << und >> Operatoren sind nicht von der Darstellung ganzer Zahlen abhängig. Sie sind als Multiplikation und Division mit Zweierpotenzen definiert und nur für nicht negative Operanden wohldefiniert. << ist für negative Operanden undefiniert, und >> ist für negative Operanden implementierungsdefiniert.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    6. November 2011 um 15:02 Uhr

  • ‘Das Negativ einer vorzeichenlosen Menge wird berechnet, indem der beförderte Wert vom größten Wert des beförderten Typs subtrahiert und eins addiert wird’ – ich weiß, dass mein mathematisches Kung-Fu im Vergleich zu den mächtigen Ritchie und Stroustrup schwach ist, aber das scheint schwach zu sein , und vielleicht verrückt, eine Situation zu handhaben, die in 99,9% der Fälle sicherlich ein Programmierfehler ist. Warum nicht einen Kompilierungsfehler ausgeben, der besagt: „Sie haben versucht, ein Zeichen auf einen unsignierten Typ anzuwenden, Sie Dingus“?

    – CCJ

    11. Dezember 2015 um 16:04 Uhr

  • @CCJ Und dann müssten Sie diese Compiler-Intelligenz auf seltsame Weise umgehen, wie z 1 + ~a den Compiler zum Schweigen zu bringen und jemand würde nicht verstehen, was dieser Ausdruck bedeutet. 🙂

    – Alexey Frunze

    12. Dezember 2015 um 5:27 Uhr

Ja, 6.2.5c9 ist genau der Absatz, den Sie gesucht haben.

  • Alternativ könnte man den negativen Operator so ansehen, als würde er die additive Inverse seines Operanden liefern. Wenn N ohne Vorzeichen ist, entspricht -N 1U+~N, da dies der Wert ist, der, wenn er zu N addiert wird, Null ergibt.

    – Superkatze

    25. Februar 2014 um 14:40 Uhr

  • @supercat Sie haben also ein garantiertes Zweierkomplementverhalten, selbst wenn die zugrunde liegende Hardware eine andere Darstellung negativer Zahlen verwendet?

    – JAB

    19. Juli 2017 um 17:02 Uhr

  • @JAB: Zweierkomplementdarstellungen stimmen zufällig mit vorzeichenlosen Darstellungen überein, aber die Definition des Verhaltens hat nichts mit Zweierkomplement zu tun.

    – Superkatze

    19. Juli 2017 um 17:31 Uhr

Benutzeravatar von Supercat
Superkatze

Das Verhalten des unären Minusoperators bei vorzeichenlosen Operanden hat nichts damit zu tun, ob eine Maschine Zweierkomplement-Arithmetik mit vorzeichenbehafteten Zahlen verwendet. Stattdessen gegeben unsigned int x,y; die Aussage y=-x; wird verursachen y um den Wert zu erhalten, den es halten müsste, um es zu machen x+y gleich Null. Wenn x ist null, y wird ebenfalls null sein. Für jeden anderen Wert von xes wird sein UINT_MAX-x+1in diesem Fall der arithmetische Wert von x+y wird sein UINT_MAX+1+(y-y) die bei Zuordnung zu a unsigned integerwerde haben UINT_MAX+1 davon subtrahiert, was Null ergibt.

Benutzeravatar von Branko Dimitrijevic
Branko Dimitrijević

In jeder Implementierung, die ich kenne, wird ein Negativ berechnet als Zweierkomplement

int a = 12;
int b = -a;
int c = ~a + 1;
assert(b == c);

… es gibt also wirklich keinen physikalischen Unterschied zwischen negativen vorzeichenbehafteten und “negativen” vorzeichenlosen Ganzzahlen – der einzige Unterschied besteht darin, wie sie sind interpretiert.

Also in diesem Beispiel…

unsigned a = 12;
unsigned b = -a;
int c = -a;

…das b und c werden genau die gleichen Bits enthalten. Der einzige Unterschied ist das b wird als 2^32-12 (oder 2^64-12) interpretiert, während c wird als “normal” -12 interpretiert.

Ein Negativ wird also unabhängig von der “Vorzeichenhaftigkeit” auf die gleiche Weise berechnet, und das Casting zwischen unsigned und signed ist eigentlich ein No-Op (und kann niemals einen Überlauf in dem Sinne verursachen, dass einige Bits “geschnitten” werden müssen -aus”).

  • Das kenne ich auch alles aus der Praxis. Die Frage bezieht sich auf eine andere Sache, das Verhalten gemäß dem Standard.

    – Alexey Frunze

    6. November 2011 um 13:19 Uhr

  • Ich wusste das nicht und es scheint Foundation-Zeug zu sein, ta. +1

    – Matt Stevens

    3. Juli 2014 um 21:30 Uhr

Benutzeravatar von Pearly
Perlig

Das ist spät, aber trotzdem…

C sagt das (ziemlich hart, wie bereits in anderen Antworten erwähnt).

  • Jeder vorzeichenlose Typ ist eine binäre Darstellung mit einer typspezifischen Anzahl von Bits

  • Alle arithmetischen Operationen an vorzeichenlosen Typen werden durchgeführt (mod 2^N), wobei ‘mod’ die mathematische Definition des Modulus ist und ‘N’ die Anzahl der Bits ist, die zur Darstellung des Typs verwendet werden.

Der unäre Minusoperator, der auf einen vorzeichenlosen Typ angewendet wird, verhält sich so, als ob der Wert auf den nächstgrößeren vorzeichenbehafteten Typ heraufgestuft, dann negiert und dann wieder in einen vorzeichenlosen Typ konvertiert und auf den Quelltyp gekürzt worden wäre. (Dies ist eine leichte Vereinfachung, da die Integer-Promotion für alle Typen erfolgt, die weniger Bits als ‘int’ haben, aber meiner Meinung nach kommt es nahe genug.)

Einige Compiler geben tatsächlich Warnungen aus, wenn sie das unäre Minus auf einen vorzeichenlosen Typ anwenden, aber das ist nur zum Vorteil des Programmierers. IMHO ist das Konstrukt wohldefiniert und portabel.

Aber verwenden Sie im Zweifelsfall einfach nicht das unäre Minus: Schreiben Sie ‘0u – x’ statt ‘-x’, und alles wird gut. Jeder anständige Codegenerator erstellt daraus nur eine Negationsanweisung, es sei denn, die Optimierung ist vollständig deaktiviert.

1394760cookie-checkC: Unäres Minus-Operatorverhalten mit vorzeichenlosen Operanden

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy