2009-11-17 7 views
20

ich einen Wert wie dieses:interpret unterzeichnet als unsigned

int64_t s_val = SOME_SIGNED_VALUE; 

Wie kann ich ein

uint64_t u_val 

erhalten, die genau das gleiche Bitmuster wie s_val hat, wird aber als unsigned behandelt?

Dies kann wirklich einfach sein, aber nach dem Blick auf Stackoverflow und anderswo habe ich die Antwort nicht angezeigt.

+0

Up-Stimmen für alle; danke für deinen Beitrag. Ich denke, dass static_cast in der Tat die richtige Antwort ist. Für den Rekord hatte ich es zuerst versucht, aber wegen eines dümmlichen Fehlers an anderer Stelle dachte ich, es würde das Bitmuster nicht erhalten. Um die Frage zu klären, ist es in Ordnung, dass s_val! = U_val (was der Fall ist, wenn s_val <0). Die Bits sind was zählt. – bbg

Antwort

28
int64_t s_val = SOME_SIGNED_VALUE; 
uint64_t u_val = static_cast<uint64_t>(s_val); 

C++ Standard-4,7/2 lautet:

Wenn der Zieltyp nicht signiert ist, der sich ergebende Wert der am wenigsten unsigned integer kongruent mit der Source-Ganzzahl ist (modulo 2 n wobei n die Anzahl der Bits, die zur Darstellung des Typs ohne Vorzeichen verwendet werden). [Anmerkung: In einer Zweierkomplementdarstellung ist diese Umwandlung konzeptionell und es gibt keine Änderung im Bitmuster (wenn keine Kürzung erfolgt). ]

Von der anderen Seite, sagt Norm, dass „die von reinterpret_cast ausgeführt Mapping Implementierung definiert ist [Anmerkung:.. Eine Darstellung unterscheidet sich von dem ursprünglichen Wert, es könnte, oder auch nicht, produzieren]“ (5.2. 10/3). Also, ich würde empfehlen, static_cast zu verwenden.

2

Sie können auch reinterpret_cast es, oder verwenden Sie ein union:

union { 
    int64_t i64; 
    uint64_t ui64; 
} variable; 

variable.i64 = SOME_SIGNED_VALUE; 
uint64_t a_copy = variable.ui64; 
+1

'static_cast' führt nicht zum Bitmusterverlust. –

+0

ist es nicht. Ich frage mich, warum nicht ... – xtofl

+1

Ich war ein bisschen besorgt über static_cast <>. Ich schlug auch vor, reinterpret_cast <> wegen der Bitmusteranweisung zu verwenden. Bist du sicher, dass static_cast <> funktioniert (ich denke es wird aber keine Kopie des Standard-handy haben). Aber reinterpret_cast <> ist auch ein Hinweis darauf, dass es sich um eine unsichere Besetzung handelt. –

6

Generell ist es egal, ob Sie static_cast<int64_t> oder reinterpret_cast<int64_t> verwenden. Solange Sie auf einem Prozessor laufen, der two's complement verwendet, um negative Zahlen darzustellen, ist das Ergebnis das gleiche. (Praktisch alle modernen Prozessoren verwenden das.) Unter dem Zweierkomplement wird eine positive Zahl in einem vorzeichenbehafteten int auf die gleiche Weise in einem unsignierten int dargestellt; Wenn es eine negative Zahl ist, wird es als eine große positive Zahl in der unsignierten Form umgedeutet.

Im Grunde teilt der Cast dem Compiler mit, dass er verschiedene Assemblierungsanweisungen erstellen soll, wenn er mit diesem Wert arbeitet. Z.B. Es gibt verschiedene Anweisungen für Multiplikation und Division für vorzeichenbehaftete Ganzzahlen. Obwohl Addition und Subtraktion gleich bleiben (lesen Sie den Wikipedia-Link und Sie werden verstehen).

+0

Aber was ist mit einem negativen Wert? –

+0

Die Regel lautet "Wenn der neue Typ nicht vorzeichenbehaftet ist, wird der Wert durch wiederholtes Hinzufügen oder Subtrahieren von einem Wert größer als der Maximalwert, der im neuen Typ dargestellt werden kann, bis der Wert im Bereich des neuen Typs liegt" für das Zweierkomplement, und ich denke, das 1-Komplement, aber ich würde nicht schwören, dass es das gleiche Bitmuster für mehr bizzaro Darstellungen von negativen Zahlen behalten hat. –

+0

Das obige stammt aus dem C99-Standard, der C++ - Standard besagt: "Der resultierende Wert ist die am wenigsten vorzeichenlose Ganzzahl, die deckungsgleich zur Quell-Ganzzahl ist (Modulo 2n, wobei n die Anzahl der Bits für den unsignierten Typ ist). In einer Zweierkomplement-Darstellung ist diese Umwandlung konzeptuell und es gibt keine Änderung im Bitmuster (wenn es keine Abschneidung gibt). " –

5

Logisches Bitmuster (Bits der Wertdarstellung), d. H. Werte von Binärziffern können nur erhalten werden, wenn der ursprüngliche vorzeichenbehaftete Wert nicht negativ war, da negative Werte nicht durch eine vorzeichenlose Ganzzahl dargestellt werden können. Alles was Sie tun müssen, ist Ihre signierten Wert Ihrem unsigned integrales Objekt zuweisen und Sie sind fertig

uint64_t u_val = s_val; 

Eine explizite Umwandlung nicht erforderlich ist, kann aber verwendet werden Compiler-Warnungen zu unterdrücken.

Wie für das physikalische Bitmuster (d. H. Was Sie im Rohspeicher sehen, Bits der Objektdarstellung), können Sie es einfach nicht auf diese Weise "konvertieren". Die C++ - Sprache bietet keine Konvertierungsmethoden, mit denen das physische Bitmuster erhalten werden könnte.Alles, was Sie von dem signierten Objekt als unsigned Objekt der gleichen Größe

STATIC_ASSERT(sizeof(int64_t) == sizeof(uint64_t)); 
uint64_t u_val = reinterpret_cast<uint64_t&>(s_val); 

Wieder der Speicher belegt ist reinterpret tun können, ist dies nicht eine Umwandlung, sondern ein Speicher Umdeutung. Dies funktioniert nicht garantiert und das ist generell illegal.

+0

Guter Punkt, um "Umwandlung" von "Interpretation" zu unterscheiden! – xtofl

+0

Wie ist es illegal? Was meinst du mit "Arbeit"? Sie können '' zurück' 'uminterpretieren, nicht wahr? – xtofl

+0

Es ist illegal, weil C++ - Sprache explizit den Zugriff auf Speicher verweigert, der von einem Objekt vom Typ "T" als Objekt vom anderen Typ "U" (mit wenigen Ausnahmen) belegt ist. Mit anderen Worten, das Lesen von neu interpretiertem Speicher ist fast immer illegal. – AnT

10

Beachten Sie, dass Sie die Besetzung überhaupt nicht benötigen. Bei all dem Streit um die Frage, ob die Darsteller bei negativen Darstellungen in die Quere kommen oder nicht, ist eine Sache verloren gegangen - die Besetzung ist völlig unnötig.

Aufgrund der Conversions, die C/C++ tun (und wie Gießen definiert), ist diese:

int64_t s_val = SOME_SIGNED_VALUE; 
uint64_t u_val = s_val; 

genau entspricht:

int64_t s_val = SOME_SIGNED_VALUE; 
uint64_t u_val = static_cast<uint64_t>(s_val); 

Das heißt, könnten Sie immer noch wollen die Besetzung, weil sie Absicht signalisiert. Allerdings habe ich gehört, dass Sie argumentieren, dass Sie nicht unnötige Umwandlungen verwenden sollten, weil es den Compiler in Situationen stummschalten kann, in denen Sie eine Warnung wünschen.

Wählen Sie Ihr Gift aus.

+1

Aha, das ist ein guter Punkt. In meinem Fall denke ich, dass es am besten ist, Absicht zu signalisieren, aber ich bin froh, den Vorbehalt zu sehen. – bbg

5

Ich stimme zu static_cast ist in diesem Fall geeignet, aber niemand hat einen sehr ähnlich aussehenden Fall erwähnt, in dem static_cast Bits nicht beibehalten würde, wie man erwarten könnte.

char x = -1; // 255 
unsigned int x2 = static_cast<unsigned int>(x); // 4294967295 
unsigned int x3 = static_cast<unsigned int>(static_cast<unsigned char>(x)); // 255 

Achten Sie auf sign extension aus, wenn Sie von einem kleinen signierten Wert zu einem großen Wert ohne Vorzeichen werfen. Möglicherweise sind auch andere Kombinationen angreifbar - ich habe es nicht ganz durchdacht.

+0

Interessanter Punkt; Danke für die Erklärung und den Link zur Zeichenerweiterung. – bbg

1

Wollte diese C++ 14 moderne, generische Lösung teilen. Ursprünglich demonstriert here.

template<class T> 
auto as_unsigned(T t) 
{ 
    return std::make_unsigned_t<T>(t); 
} 

, die wie folgt verwendet werden:

auto sx = int32_t{ 55 }; 
auto ux = as_unsigned(sx); 

Sie es here in Aktion sehen können.

Verwandte Themen