Was ist mit bitweisen Operatoren und Integer-Promotion los?

Lesezeit: 7 Minuten

Was ist mit bitweisen Operatoren und Integer Promotion los
Wandernder Narr

Ich habe ein einfaches Programm. Beachten Sie, dass ich eine vorzeichenlose Ganzzahl mit fester Breite und einer Größe von 1 Byte verwende.

#include <cstdint>
#include <iostream>
#include <limits>

int main()
{
    uint8_t x = 12;
    std::cout << (x << 1) << 'n';
    std::cout << ~x;

    std::cin.clear();
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), 'n');
    std::cin.get();

    return 0;
}

Meine Ausgabe ist die folgende.

24
-13

Ich habe größere Zahlen und Operatoren getestet << gibt mir immer positive Zahlen, während Operator ~ gibt mir immer negative Zahlen. habe ich dann verwendet sizeof() und gefunden…

Wenn ich den bitweisen Linksverschiebungsoperator (<<), erhalte ich eine vorzeichenlose 4-Byte-Ganzzahl.

Wenn ich den bitweisen Not-Operator verwende (~), erhalte ich eine vorzeichenbehaftete 4-Byte-Ganzzahl.

Es scheint, dass der bitweise Not-Operator (~) führt eine vorzeichenbehaftete ganzzahlige Beförderung durch, wie es die arithmetischen Operatoren tun. Der Linksverschiebungsoperator (<<) scheint zu einem vorzeichenlosen Integral zu werden.

Ich fühle mich verpflichtet zu wissen, wenn der Compiler hinter meinem Rücken etwas ändert. Wenn ich mit meiner Analyse richtig liege, werden alle bitweisen Operatoren zu einer 4-Byte-Ganzzahl befördert? Und warum sind manche signiert und manche unsigniert? Ich bin so verwirrt!

Bearbeiten: Meine Annahme, immer positive oder immer negative Werte zu bekommen, war falsch. Aber da ich mich geirrt habe, verstehe ich, was wirklich passiert ist, dank der großartigen Antworten unten.

  • Wie haben Sie Streams zur Ausgabe Ihrer uint8_t als Zahl statt als Zeichen? Sind Sie sicher, dass Ihr Compiler diesen Typ nicht als Alias ​​verwendet int?

    – Anton Samsonow

    27. Mai ’15 um 5:50 Uhr

  • @AntonSamsonov In der folgenden Antwort erklärt er dies als Ergebnis der integralen Beförderung, die nach der bitweisen Operation erfolgt. Mit anderen Worten, der Datentyp wurde von a heraufgestuft uint8_t zu einem int.

    – Wandernder Narr

    27. Mai ’15 um 6:03 Uhr


Was ist mit bitweisen Operatoren und Integer Promotion los
Benutzer657267

[expr.unary.op]

Der Operand von ~ muss einen ganzzahligen oder unbegrenzten Aufzählungstyp haben; das Ergebnis ist das Einerkomplement seines Operanden. Integrale Beförderungen werden durchgeführt.

[expr.shift]

Die Schichtoperatoren << und >> Gruppe von links nach rechts. […] Die Operanden müssen vom Aufzählungstyp ganzzahlig oder ohne Bereich sein und integrale Beförderungen durchgeführt werden.

Was ist die integrale Förderung von uint8_t (was normalerweise der Fall sein wird unsigned_char hinter den Kulissen)?

[conv.prom]

Ein prvalue eines ganzzahligen Typs außer bool, char16_t, char32_toder
wchar_t deren ganzzahliger Umwandlungsrang (4.13) kleiner ist als der Rang von
int kann in einen Prvalue vom Typ konvertiert werden int wenn int kann alle Werte des Quelltyps darstellen; andernfalls kann der Quell-prvalue in einen prvalue vom Typ konvertiert werden unsigned int.

So intweil alle Werte von a uint8_t vertreten werden kann durch int.

Was ist int(12) << 1 ? int(24).

Was ist ~int(12) ? int(-13).

  • Und der Grund, warum ich alle negativen Zahlen bekam, nachdem ich den bitweisen Not-Operator (~) liegt daran, dass die meisten Binärzahlen nachgestellte 0 von der linken Seite haben und wenn sie umgedreht werden, wird die Ziffer ganz links wahrscheinlich 1 sein, was die Zahl negativ macht. Dies gilt insbesondere, wenn der Speicher, der den Wert enthält, 4 Byte groß ist, was mir 2 ^ 32 mögliche Werte gibt, und wenn der von mir gewählte Wert viel kleiner als dieser Bereich ist.

    – Wandernder Narr

    27. Mai 15 um 5:58 Uhr


  • Es sieht so aus, als hätten Sie diese Compiler-Informationen aus einem ausführlichen C++-Buch oder Handbuch gefunden. Wenn ja, wie heißt es? Ich möchte es als Ressource verwenden, wenn ich bei Compiler-Operationen hängen bleibe.

    – Wandernder Narr

    27. Mai ’15 um 6:22 Uhr


  • @WanderingIdiot “von links nachlaufend” heißt “führend”. Das Buch heißt C++-Standard (nicht eine empfohlene Ressource). de.cppreference.com/w/cpp/language/…

    – Marc Glisse

    27. Mai ’15 um 6:42 Uhr

  • @ WanderingIdiot Das Heilige Dokument des C++-Gesetzes

    – fredoverflow

    27. Mai ’15 um 6:43 Uhr

  • @DrumM Jedes anständige C ++ – Einführungsbuch sollte die meisten davon abdecken, z stroustrup.com/4th.html

    – Benutzer657267

    30. März 19 um 12:55 Uhr

1643341811 876 Was ist mit bitweisen Operatoren und Integer Promotion los
6502

Aus Performance-Gründen die Sprache C und C++ berücksichtigen int der “natürlichste” Integer-Typ zu sein und stattdessen Typen, die “kleiner” als an sind int gelten als eine Art „Speicher“-Typ.

Wenn Sie einen Speichertyp in einem Ausdruck verwenden, wird er automatisch in einen umgewandelt int oder zu einem unsigned int implizit. Beispielsweise:

// Assume a char is 8 bit
unsigned char x = 255;
unsigned char one = 1;

int y = x + one; // result will be 256 (too large for a byte!)
++x;             // x is now 0

Was passiert ist, ist das x und one im ersten Ausdruck wurden implizit in ganze Zahlen konvertiert, die Addition wurde berechnet und das Ergebnis wurde in eine ganze Zahl zurückgespeichert. Mit anderen Worten, die Berechnung wurde NICHT mit zwei Zeichen ohne Vorzeichen durchgeführt.

Ebenso, wenn Sie eine haben float Wert in einem Ausdruck wird der Compiler als erstes zu einem hochstufen double (mit anderen Worten float ist ein Speichertyp und double ist stattdessen die natürliche Größe für Fließkommazahlen). Dies ist der Grund, warum Sie verwenden printf Floats zu drucken braucht man nicht zu sagen %lf int die Formatzeichenfolgen und %f reicht (%lf wird benötigt für scanf aber weil diese Funktion speichert ein Ergebnis und ein float kann kleiner als a sein double).

C++ hat die Sache ziemlich verkompliziert, weil Sie beim Übergeben von Parametern an Funktionen unterscheiden können ints und kleinere Typen. Daher ist es nicht IMMER wahr, dass in jedem Ausdruck eine Konvertierung durchgeführt wird … zum Beispiel können Sie haben:

void foo(unsigned char x);
void foo(int x);

wo

unsigned char x = 255, one = 1;
foo(x);       // Calls foo(unsigned char), no promotion
foo(x + one); // Calls foo(int), promotion of both x and one to int

  • Mir hat Ihre letzte Bemerkung darüber gefallen, dass beim Übergeben von Funktionsparametern keine impliziten Konvertierungen durchgeführt werden. Das sind einige nützliche Informationen, danke.

    – Wandernder Narr

    27. Mai ’15 um 6:18 Uhr

  • Was ist daran so “natürlich”? Nun, der Compiler kann tun, was er braucht / will, um das Ergebnis zu berechnen (auf dem schnellstmöglichen Weg oder unter Berücksichtigung anderer Überlegungen), aber er sollte den (größten) Operandentyp beibehalten. Nur der Programmierer soll den Ausdruckstyp erweitern – mit einer expliziten Umwandlung; jedes andere Verhalten ist kontraintuitiv. Oder ist es aus Ihrer Sicht “natürlicher”, explizite Umwandlungen um alle Ausdrücke zu schreiben, um sie wieder in ihre ursprüngliche Domäne zu versetzen?

    – Anton Samsonow

    27. Mai ’15 um 7:43 Uhr

  • “Ebenso, wenn Sie einen Float-Wert in einem Ausdruck haben, wird der Compiler ihn als erstes zu einem Double hochstufen” – Das ist in C++ nicht wahr. Es passiert nur im Rahmen von Standardargument-Promotions (Absatz [5.2.2p7] im Standard), die nur auf Funktionsargumente angewendet werden, die mit dem übereinstimmen Ellipsenparameterspezifikation (...weshalb es passiert für printf). In a + bwenn beides a und b sind floatwird weder heraufgestuft noch der Typ des Ergebnisses float; wenn einer ist float und das andere doubledann passiert die Konvertierung, aber das ist etwas anderes.

    – Boddan

    27. Mai ’15 um 8:18 Uhr

  • @bogdan: eine Funktion tut a = b + c wo a, b und c sind float erzeugt den gleichen identischen Byte-für-Byte-Maschinencode wie man es stattdessen tut a = (double)b + (double)c.

    – 6502

    27. Mai ’15 um 9:01 Uhr


  • Das ist ein Implementierungsdetail, und es gilt nicht einmal für alle Implementierungen. Ich habe gerade überprüft, dass MSVC12 generiert sehr unterschiedlicher Code für Ihre beiden Fälle. Es generiert auch eine Warnung für die Konvertierung von double zu float für Ihren zweiten Fall (am /W4), was im ersten Fall nicht der Fall ist. Sie können das überprüfen b + c Typ hat float durch die Nutzung std::is_same<decltype(a + b), float>::value.

    – Boddan

    27. Mai ’15 um 10:09 Uhr

1643341811 913 Was ist mit bitweisen Operatoren und Integer Promotion los
Jean-Baptiste Yunes

Ich habe größere Zahlen getestet und der Operator << gibt mir immer positive Zahlen, während der Operator ~ mir immer negative Zahlen gibt. Ich habe dann sizeof() verwendet und gefunden ...

Falsch, teste es:

uint8_t v = 1;
for (int i=0; i<32; i++) cout << (v<<i) << endl;

gibt:

1
2
4
8
16
32
64
128
256
512
1024
2048
4096
8192
16384
32768
65536
131072
262144
524288
1048576
2097152
4194304
8388608
16777216
33554432
67108864
134217728
268435456
536870912
1073741824
-2147483648

uint8_t ist ein 8 Bit langer vorzeichenloser Ganzzahltyp, der Werte im Bereich darstellen kann [0,255]da dieser Bereich im Bereich von enthalten ist int es wird gefördert int (nicht unsigned int). Beförderung zu int hat Vorrang vor der Beförderung zu unsigned.

1643341811 900 Was ist mit bitweisen Operatoren und Integer Promotion los
dev_ankit

Einblick in Zweierkomplement und wie der Computer negative ganze Zahlen speichert.
Versuche dies

#include <cstdint>
#include <iostream>
#include <limits>
int main()
{
uint8_t x = 1;
int shiftby=0;
shiftby=8*sizeof(int)-1;
std::cout << (x << shiftby) << 'n'; // or std::cout << (x << 31) << 'n';

std::cout << ~x;

std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), 'n');
std::cin.get();
return 0;
}

Die Ausgabe ist -2147483648

Wenn das erste Bit einer vorzeichenbehafteten Zahl 1 ist, wird dies im Allgemeinen als negativ angesehen. wenn Sie eine große Zahl nehmen und verschieben. Wenn Sie es so verschieben, dass das erste Bit 1 ist, wird es negativ

** BEARBEITEN **

Nun, ich kann mir einen Grund vorstellen, warum Shift-Operatoren unsigned int verwenden würden. Betrachten Sie den Rechtsverschiebungsbetrieb >> Wenn Sie nach rechts verschieben, erhalten Sie -12 122 statt -6. Dies liegt daran, dass am Anfang eine Null hinzugefügt wird, ohne das Vorzeichen zu berücksichtigen

.

669340cookie-checkWas ist mit bitweisen Operatoren und Integer-Promotion los?

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

Privacy policy