Warum wird in binären Vorgängen mit gemischter Signedheit der C ++ - Standard für vorzeichenbehaftete Ganzzahlen auf vorzeichenlos gesetzt?

8

Die C- und C ++ - Standards schreiben vor, dass in Binäroperationen zwischen einer vorzeichenbehafteten und einer vorzeichenlosen Ganzzahl des gleichen Rangs die vorzeichenbehaftete Ganzzahl in unsigned umgewandelt wird. Es gibt viele Fragen zu SO, die dadurch verursacht werden ... nennen wir es seltsames Verhalten: unsigned to signed conversion , implizite C ++ - Konvertierung (signiert + unsigniert) , Eine Warnung - Vergleich zwischen Ganzzahlausdrücken mit und ohne Vorzeichen , < a href="https://stackoverflow.com/questions/12877988/mod-with-mixed-signigness">% (mod) mit gemischter Signedness , etc.

Aber keiner von diesen gibt irgendwelche Gründe, warum der Standard diesen Weg geht, anstatt auf unterschriebene Ints zu setzen. Ich habe einen selbst ernannten Guru gefunden, der sagt, dass es das offensichtlich Richtige ist, aber er gibt auch keine Begründung: Ссылка .

Wenn ich meinen eigenen Code durchsuche, muss ich, egal wo ich binsierte und vorzeichenlose Ganzzahlen kombiniere, immer von nicht signiert auf signiert umwandeln. Es gibt Orte, an denen es keine Rolle spielt, aber ich habe kein einziges Codebeispiel gefunden, bei dem es sinnvoll ist, die vorzeichenbehaftete Ganzzahl vorzeichenlos zu machen.

Was sind Cases, bei denen das Casting zu unsigned führt? Warum ist der Standard so?

    
Cris Luengo 11.04.2017, 03:22
quelle

2 Antworten

9

Casting von unsigned zu signed führt zu implementierungsdefiniertem Verhalten, wenn der Wert nicht dargestellt werden kann. Casting von vorzeichenlos nach vorzeichenlos ist immer modulo zwei auf die Macht der unsigned Bitsize, so dass es immer gut definiert ist.

Die Standardkonvertierung bezieht sich auf den signierten Typ, wenn jeder mögliche vorzeichenlose Wert im signierten Typ darstellbar ist. Andernfalls wird der Typ ohne Vorzeichen ausgewählt. Dies garantiert, dass die Konvertierung immer klar definiert ist.

Notizen

  1. Wie in den Kommentaren angegeben, wurde der Konvertierungsalgorithmus für C ++ von C geerbt, um Kompatibilität zu erhalten, was technisch gesehen der Grund dafür ist, dass es in C ++ ist.

  2. Es wurde vorgeschlagen, dass die Entscheidung im Standard, vorzeichenlose Konvertierungen zu definieren und nicht vorzeichenkonform zu konvertieren, irgendwie willkürlich ist und dass die andere mögliche Entscheidung symmetrisch wäre. Die mögliche Konvertierung ist jedoch nicht symmetrisch.

    In beiden Nicht-Zweierkomplementdarstellungen, die von dem Standard in Betracht gezogen werden, kann eine mit n -bit signierte Darstellung nur 2 n darstellen -1 Werte, während eine vorzeichenlose Darstellung n -Bit 2 n -Werte darstellen kann. Folglich ist eine Konvertierung von vorzeichenbehaftet nach vorzeichenlos verlustfrei und kann umgekehrt werden (obwohl ein vorzeichenloser Wert niemals erzeugt werden kann). Bei der Konvertierung von unsigned in signed müssen dagegen zwei verschiedene vorzeichenlose Werte auf dasselbe vorzeichenbehaftete Ergebnis reduziert werden.

    In einem Kommentar wird die Formel sint = uint > sint_max ? uint - uint_max : uint vorgeschlagen. Dies koalesziert die Werte uint_max und 0; beide sind auf 0 abgebildet. Das ist ein wenig komisch, auch für Nicht-2s-Komplement-Repräsentationen, aber für Zweierkomplement ist es unnötig und, noch schlimmer, es erfordert, dass der Compiler Code ausgibt, um diese unnötige Verschmelzung mühsam zu berechnen. Im Gegensatz dazu ist die vorzeichenbehaftete Konvertierung des Standards verlustfrei und im allgemeinen Fall (Zweierkomplement-Architekturen) ist es ein No-Op.

rici 11.04.2017, 03:27
quelle
1

Das ist eine Art Halbantwort, weil ich die Argumentation des Ausschusses nicht wirklich verstehe.

Aus dem Begründungstext des C90-Ausschusses: Ссылка

  

Seit der Veröffentlichung von K & amp; R ist eine ernsthafte Divergenz zwischen Implementierungen von C in der Entwicklung von integralen Promotionsregeln aufgetreten. Implementierungen fallen in zwei Hauptlager, die als unsigniert erhalten und wertschonend charakterisiert werden können. Der Unterschied zwischen diesen Ansätzen liegt in der Behandlung von unsigned char und unsigned short , wenn sie durch die Integral Promotions erweitert werden, aber die Entscheidung hat auch Auswirkungen auf die Typisierung von Konstanten (siehe §3.1. 3.2).

... und anscheinend auch bei den Konvertierungen, die vorgenommen wurden, um die beiden Operanden für jeden Operator abzugleichen. Es geht weiter:

  

Beide Schemata liefern die gleiche Antwort in der überwiegenden Mehrheit der Fälle, und beide ergeben das gleiche effektive Ergebnis in noch mehr Fällen in Implementierungen mit Zweierkomplement-Arithmetik und ruhigem Umlauf bei vorzeichenbehaftetem Überlauf - das heißt, in den meisten aktuellen Implementierungen .

Es spezifiziert dann einen Fall, in dem Mehrdeutigkeit der Interpretation entsteht, und sagt:

  

Das Ergebnis muss fraglich signiert getauft werden, da entweder die vorzeichenbehaftete oder die unsignierte Interpretation verwendet werden kann. Genau die gleiche Mehrdeutigkeit tritt auf, wenn ein unsigned int einem signed int über einen Operator gegenübersteht und der signed int einen negativen Wert hat. (Keines der beiden Systeme verbessert oder löst die Mehrdeutigkeit dieser Konfrontation.) Plötzlich wird das negative signed int zu einem sehr großen unsigned int , was überraschend sein kann --- oder es kann genau das sein, was gewünscht wird von einem erfahrenen Programmierer. Natürlich können all diese Unklarheiten durch eine umsichtige Verwendung von Abgüssen vermieden werden.

und:

  

Die unsignierten Erhaltungsregeln erhöhen die Anzahl der Situationen, in denen unsigned int mit signed int konfrontiert ist, zu einem fraglich signierten Ergebnis, während die werterhaltenden Regeln solche Konfrontationen minimieren. Daher wurden die wertbewahrenden Regeln für den unerfahrenen oder unvorsichtigen Programmierer als sicherer angesehen. Nach vielen Diskussionen entschied sich der Ausschuss für wertschöpfende Regeln, trotz der Tatsache, dass sich die UNIX-C-Compiler in Richtung unsignierter Erhaltung entwickelt hatten.

Somit betrachten sie den Fall von int + unsigned als unerwünschte Situation und wählen Konvertierungsregeln für char und short , die möglichst wenige dieser Situationen ergeben, obwohl die meisten Compiler zu der Zeit folgten ein anderer Ansatz. Wenn ich richtig verstehe, hat diese Wahl sie gezwungen, der aktuellen Wahl von int + unsigned zu folgen und eine Operation unsigned zu erreichen.

Ich finde das alles immer noch wirklich bizarr.

    
Cris Luengo 12.04.2017 18:03
quelle

Tags und Links