8 Stimmen

Die sauberste Art, zwei Shorts zu einem int zu kombinieren

Ich habe zwei 16-Bit-Shorts (s1 und s2) und versuche, sie zu einer einzigen 32-Bit-Ganzzahl (i1) zusammenzufassen. Gemäß der Spezifikation, mit der ich zu tun habe, ist s1 das höchstwertige Wort und s2 das niederwertige Wort, und das kombinierte Wort scheint vorzeichenbehaftet zu sein. (d.h. das oberste Bit von s1 ist das Vorzeichen.)

Wie lassen sich s1 und s2 am saubersten kombinieren?

Ich dachte an etwas wie

const utils::int32 i1 = ((s1<<16) | (s2));

tun würde, und es scheint zu funktionieren, aber ich bin besorgt über die Linksverschiebung eines Kurzschlusses um 16.

Ich bin auch an der Idee interessiert, eine Gewerkschaft mit der Arbeit zu beauftragen. Haben Sie eine Meinung dazu, ob dies eine gute oder schlechte Idee ist?

14voto

Was Sie tun, ist nur sinnvoll, wenn die Shorts und der int alle vorzeichenlos sind. Wenn eine der Shorts vorzeichenbehaftet ist und einen negativen Wert hat, ist die Idee, sie zu einem einzigen int zu kombinieren, sinnlos, es sei denn, Sie haben eine domänenspezifische Spezifikation, um eine solche Eventualität abzudecken.

8voto

Mark Ransom Punkte 283960

Was Sie haben, sieht fast korrekt, aber wird wahrscheinlich fehlschlagen, wenn der zweite Teil negativ ist; die implizite Umwandlung in int wird wahrscheinlich Vorzeichen erweitern und füllen die oberen 16 Bits mit Einsen. Ein Cast zu unsigned short würde wahrscheinlich verhindern, dass das passiert, aber der beste Weg, um sicher zu sein, ist, die Bits zu maskieren.

const utils::int32 combineddata = ((data.first<<16) | ((data.second) & 0xffff));

6voto

Lundin Punkte 171916

Ich weiß, dass dies ein alter Beitrag ist, aber die Qualität der gegenwärtig geposteten Antworten ist deprimierend...

Dies sind die Themen, die es zu berücksichtigen gilt:

  • Implizite Integer-Promotion von Shorts (oder anderen kleinen Integer-Typen) führt zu einem Operanden vom Typ int die unterzeichnet ist. Dies geschieht unabhängig von der Vorzeichenbehaftung des kleinen Ganzzahlentyps. Integer-Promotion geschieht bei den Schiebeoperationen und beim bitweisen ODER.
  • Im Falle der Verschiebeoperatoren ist der resultierende Typ der des verschobenen linken Operanden. Im Falle von bitweisem ODER ergibt sich der Ergebnistyp aus den "üblichen arithmetischen Umrechnungen".
  • Die Linksverschiebung einer negativen Zahl führt zu einem undefinierten Verhalten. Die Rechtsverschiebung einer negativen Zahl führt zu einem durch die Implementierung definierten Verhalten (logische oder arithmetische Verschiebung). Daher sollten vorzeichenbehaftete Zahlen in 99 % aller Anwendungsfälle nicht zusammen mit Bitverschiebungen verwendet werden.
  • Unions, Arrays und Ähnliches sind schlechte Lösungen, da sie den Code endianess-abhängig machen. Außerdem ist das Typ-Punning durch Unions in C++ (im Gegensatz zu C) kein wohldefiniertes Verhalten. Zeigerbasierte Lösungen sind schlecht, da sie gegen die "strenge Aliasing-Regel" verstoßen.

Daher wird eine angemessene Lösung gefunden:

  • Verwenden Sie Operanden mit Typen, die garantiert vorzeichenlos sind und nicht implizit befördert werden.
  • Verwenden Sie Bit-Verschiebungen, da diese endianess-unabhängig sind.
  • Nicht irgendeinen nicht portablen Quatsch mit Unions oder Zeigern verwenden. Mit solchen Lösungen ist absolut nichts gewonnen, außer der Nicht-Portabilität. Solche Lösungen sind jedoch wahrscheinlich, um eine oder mehrere Fälle von undefiniertem Verhalten aufzurufen.

Sie wird folgendermaßen aussehen:

int32_t  i32 = (int32_t)( (uint32_t)s1<<16 | (uint32_t)s2 );

Jede andere Lösung ist höchst fragwürdig und bestenfalls nicht tragbar.

3voto

Jonathan Punkte 12856

Da es niemand gepostet hat, würde die Gewerkschaft folgendermaßen aussehen. Aber die Kommentare über die Endian-ness gelten definitiv.

Big-Endian:

typedef union {
    struct {
        uint16_t high;
        uint16_t low;
    } pieces;
    uint32_t all;
} splitint_t;

Little-Endian:

typedef union {
    struct {
        uint16_t low;
        uint16_t high;
    } pieces;
    uint32_t all;
} splitint_t;

1voto

twk Punkte 3094

Versuchen Sie, data.second explizit auf einen kurzen Typ zu projizieren, z. B.:

const utils::int32 combineddata = ((data.first<<16) | ((short)data.second));

Bearbeiten: Ich bin C#-Entwickler, wahrscheinlich das Casting in Ihrem Code-Sprache sieht anders, aber Idee könnte die gleiche sein.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X