Ich möchte eine Funktion definieren, die ein unsigned int
as-Argument verwendet und ein int
kongruentes Modulo UINT_MAX + 1 an das Argument zurückgibt.
Ein erster Versuch könnte so aussehen:
int unsigned_to_signed(unsigned n)
{
return static_cast<int>(n);
}
Wie jeder Sprachanwalt weiß, ist das Casting von nicht signiert zu signiert für Werte größer als INT_MAX implementierungsdefiniert.
Ich möchte dies so implementieren, dass (a) es nur auf dem von der Spezifikation vorgeschriebenen Verhalten beruht; und (b) es wird auf jeder modernen Maschine zu einem No-Op kompiliert und der Compiler optimiert.
Was bizarre Maschinen betrifft ... Wenn es kein vorzeichenbehaftetes int-kongruentes Modulo UINT_MAX + 1 zum vorzeichenlosen int gibt, nehmen wir an, ich möchte eine Ausnahme auslösen. Wenn es mehr als eine gibt (ich bin nicht sicher, ob dies möglich ist), nehmen wir an, ich möchte die größte.
OK, zweiter Versuch:
int unsigned_to_signed(unsigned n)
{
int int_n = static_cast<int>(n);
if (n == static_cast<unsigned>(int_n))
return int_n;
// else do something long and complicated
}
Die Effizienz ist mir egal, wenn ich mich nicht in einem typischen Zweierkomplementsystem befinde, da dies meiner bescheidenen Meinung nach unwahrscheinlich ist. Und wenn mein Code zu einem Engpass bei den allgegenwärtigen Vorzeichengrößen-Systemen von 2050 wird, kann jemand das herausfinden und dann optimieren.
Dieser zweite Versuch kommt dem, was ich will, ziemlich nahe. Obwohl die Umwandlung in int
für einige Eingaben implementierungsdefiniert unsigned
ist, wird die Umwandlung in durch den Standard garantiert, um den Wert modulo UINT_MAX + 1 beizubehalten. Die Bedingung überprüft also genau, was ich will, und wird auf keinem System, auf das ich wahrscheinlich stoße, zu nichts kompiliert.
Allerdings ... Ich bin immer noch dabei, int
ohne vorher zu prüfen, ob es ein implementierungsdefiniertes Verhalten hervorruft. Auf einem hypothetischen System im Jahr 2050 könnte es wer-weiß-was tun. Nehmen wir also an, ich möchte das vermeiden.
Frage: Wie soll mein "dritter Versuch" aussehen?
Um es noch einmal zusammenzufassen: Ich möchte:
- Umwandlung von unsigned int in signated int
- Behalten Sie den Wert mod UINT_MAX + 1 bei
- Rufen Sie nur Standardverhalten auf
- Kompilieren Sie auf einem typischen Zwei-Komplement-Computer mit optimiertem Compiler zu einem No-Op
[Aktualisieren]
Lassen Sie mich ein Beispiel geben, um zu zeigen, warum dies keine triviale Frage ist.
Stellen Sie sich eine hypothetische C ++ - Implementierung mit den folgenden Eigenschaften vor:
sizeof(int)
gleich 4sizeof(unsigned)
gleich 4INT_MAX
entspricht 32767INT_MIN
gleich -2 32 + 32768UINT_MAX
gleich 2 32 - 1- Arithmetik
int
ist Modulo 2 32 (in den BereichINT_MIN
durchINT_MAX
) std::numeric_limits<int>::is_modulo
ist wahr- Das Casting ohne Vorzeichen
n
in int behält den Wert für 0 <= n <= 32767 bei und ergibt ansonsten Null
Bei dieser hypothetischen Implementierung gibt es genau einen int
kongruenten Wert (mod UINT_MAX + 1) für jeden unsigned
Wert. Meine Frage wäre also klar definiert.
Ich behaupte, dass diese hypothetische C ++ - Implementierung vollständig den Spezifikationen von C ++ 98, C ++ 03 und C ++ 11 entspricht. Ich gebe zu, dass ich nicht jedes Wort von allen auswendig gelernt habe ... Aber ich glaube, ich habe die relevanten Abschnitte sorgfältig gelesen. Wenn Sie also möchten, dass ich Ihre Antwort akzeptiere, müssen Sie entweder (a) eine Spezifikation zitieren, die diese hypothetische Implementierung ausschließt, oder (b) sie korrekt behandeln.
In der Tat muss eine korrekte Antwort jede vom Standard zugelassene hypothetische Implementierung behandeln. Das ist per Definition "nur standardmäßiges Verhalten aufrufen".
Beachten Sie übrigens, dass dies std::numeric_limits<int>::is_modulo
hier aus mehreren Gründen völlig nutzlos ist. Zum einen kann dies true
auch dann der Fall sein , wenn Casts ohne Vorzeichen für große Werte ohne Vorzeichen nicht funktionieren. Zum anderen kann es sich true
sogar um ein Komplement- oder ein Vorzeichengrößen-System handeln, wenn die Arithmetik einfach über den gesamten ganzzahligen Bereich modulo ist. Und so weiter. Wenn Ihre Antwort von abhängt is_modulo
, ist es falsch.
[Update 2]
Die Antwort von hvd hat mir etwas beigebracht: Meine hypothetische C ++ - Implementierung für Ganzzahlen ist im modernen C nicht zulässig. Die Standards C99 und C11 sind sehr spezifisch in Bezug auf die Darstellung vorzeichenbehafteter Ganzzahlen. in der Tat erlauben sie nur Zweierkomplement, Einkomplement und Vorzeichengröße (Abschnitt 6.2.6.2 Absatz (2);).
Aber C ++ ist nicht C. Wie sich herausstellt, steht diese Tatsache im Mittelpunkt meiner Frage.
Der ursprüngliche C ++ 98-Standard basierte auf dem viel älteren C89, der besagt (Abschnitt 3.1.2.5):
Für jeden der vorzeichenbehafteten Integer-Typen gibt es einen entsprechenden (aber unterschiedlichen) vorzeichenlosen Integer-Typ (gekennzeichnet mit dem Schlüsselwort unsigned), der dieselbe Speichermenge (einschließlich Vorzeicheninformationen) verwendet und dieselben Ausrichtungsanforderungen hat. Der Bereich nichtnegativer Werte eines vorzeichenbehafteten Ganzzahltyps ist ein Unterbereich des entsprechenden vorzeichenlosen Ganzzahltyps, und die Darstellung desselben Werts in jedem Typ ist dieselbe.
C89 sagt nichts darüber aus, nur ein Vorzeichenbit zu haben oder nur Zweierkomplement / Einkomplement / Vorzeichengröße zuzulassen.
Der C ++ 98-Standard hat diese Sprache fast wörtlich übernommen (Abschnitt 3.9.1 Absatz (3)):
Für jeden der vorzeichenbehafteten Ganzzahltypen gibt es einen entsprechenden (aber unterschiedlichen) vorzeichenlosen Ganzzahltyp : "
unsigned char
", "unsigned short int
", "unsigned int
" und "unsigned long int
", von denen jeder dieselbe Speichermenge belegt und dieselben Ausrichtungsanforderungen hat (3.9 ) als entsprechenden vorzeichenbehafteten Integer-Typ; Das heißt, jeder vorzeichenbehaftete Ganzzahltyp hat dieselbe Objektdarstellung wie sein entsprechender vorzeichenloser Ganzzahltyp . Der Bereich nichtnegativer Werte eines vorzeichenbehafteten Ganzzahltyps ist ein Unterbereich des entsprechenden vorzeichenlosen Ganzzahltyps, und die Wertdarstellung jedes entsprechenden vorzeichenbehafteten / vorzeichenlosen Typs muss gleich sein.
Der C ++ 03-Standard verwendet im Wesentlichen dieselbe Sprache wie C ++ 11.
Soweit ich das beurteilen kann, beschränkt keine Standard-C ++ - Spezifikation ihre vorzeichenbehafteten Ganzzahldarstellungen auf eine C-Spezifikation. Und es gibt nichts, was ein einzelnes Vorzeichenbit oder etwas Ähnliches vorschreibt. Es heißt nur, dass nicht negativ vorzeichenbehaftete Ganzzahlen ein Unterbereich der entsprechenden vorzeichenlosen Ganzzahlen sein müssen.
Also wieder behaupte ich, dass INT_MAX = 32767 mit INT_MIN = -2 32 +32768 erlaubt ist. Wenn Ihre Antwort etwas anderes voraussetzt, ist sie falsch, es sei denn, Sie zitieren einen C ++ - Standard, der mich als falsch erweist.
int
mindestens 33 Bit erforderlich sind, um sie darzustellen. Ich weiß, dass es sich nur um eine Fußnote handelt, daher kann man argumentieren, dass sie nicht normativ ist, aber ich denke, dass Fußnote 49 in C ++ 11 wahr sein soll (da es sich um eine Definition eines im Standard verwendeten Begriffs handelt) und nicht widerspricht alles, was ausdrücklich im normativen Text angegeben ist. Alle negativen Werte müssen also durch ein Bitmuster dargestellt werden, in dem das höchste Bit gesetzt ist, und daher können Sie sie nicht 2^32 - 32768
in 32 Bits packen. Nicht, dass Ihr Argument in irgendeiner Weise von der Größe abhängt int
.