Wie kombiniert man zwei 32-Bit-Ganzzahlen zu einer 64-Bit-Ganzzahl?

Lesezeit: 2 Minuten

Benutzer-Avatar
bei

Ich habe ein Zählregister, das aus zwei 32-Bit-Ganzzahlen ohne Vorzeichen besteht, eine für die höheren 32 Bits des Werts (höchstwertiges Wort) und eine andere für die niedrigeren 32 Bits des Werts (niederwertiges Wort).

Was ist der beste Weg in C, um diese beiden 32-Bit-Ganzzahlen ohne Vorzeichen zu kombinieren und dann als große Zahl anzuzeigen?

Im Einzelnen:

leastSignificantWord = 4294967295; //2^32-1

printf("Counter: %u%u", mostSignificantWord,leastSignificantWord);

Dies würde gut drucken.

Wenn die Zahl auf 4294967296 erhöht wird, habe ich es so, dass das leastSignificantWord auf 0 löscht und das mostSignificantWord (anfänglich 0) jetzt 1 ist. Der gesamte Zähler sollte jetzt 4294967296 anzeigen, aber im Moment zeigt er nur 10 an, weil ich nur verkette 1 von mostSignificantWord und 0 von leastSignificantWord.

Wie soll ich 4294967296 anstelle von 10 anzeigen lassen?

  • Ihre Methode würde nur Sinn machen, wenn Sie %8.8X als Formatbezeichner verwenden, um die Hexadezimalwerte der Wörter zu verketten. Damit es dezimal funktioniert, müsste der maximale ganzzahlige Wert (10^n)-1 sein, wobei n die Anzahl der Dezimalstellen ist, und das wird auf einer Binärmaschine niemals der Fall sein!

    – Clifford

    4. Mai 2010 um 21:34 Uhr


Benutzer-Avatar
jfs

Es kann von Vorteil sein, es zu verwenden ohne Vorzeichen ganze Zahlen mit explizit Größen in diesem Fall:

#include <stdio.h>
#include <inttypes.h>

int main(void) {
  uint32_t leastSignificantWord = 0;
  uint32_t mostSignificantWord = 1;
  uint64_t i = (uint64_t) mostSignificantWord << 32 | leastSignificantWord;
  printf("%" PRIu64 "\n", i);

  return 0;
}

Ausgabe

4294967296

Zusammenbruch von (uint64_t) mostSignificantWord << 32 | leastSignificantWord

  • (typename) tut Typisierung in C. Es ändert den Wertdatentyp in typename.

    (uint64_t) 0x00000001 -> 0x000000000000001

  • << tut Linksverschiebung. In C führt eine Linksverschiebung bei vorzeichenlosen Ganzzahlen durch logische Verschiebung.

    0x0000000000000001 << 32 -> 0x0000000100000000

linke logische Verschiebung

  • Brillant. Jemand, der nicht nur weiß, wie man die C99-Typen mit expliziten Größen verwendet, sondern der auch versteht, wie man die verwandten verwendet printf Makros. +10, wenn ich könnte. SO-ers, vereinigt euch! Diese Antwort verdient eine Top-Abrechnung!

    – Norman Ramsey

    5. Mai 2010 um 0:11 Uhr

  • Könnte jemand aufschlüsseln, was diese Zeile bedeutet? uint64_t i = (uint64_t) MostSignificantWord << 32 | am wenigsten signifikantes Wort; Und was ist der PRIu64? Mein Compiler scheint es nicht zu mögen, sagt "expected ) nach PRIu64.

    – bei

    5. Mai 2010 um 15:59 Uhr

  • @Bei337: inttypes.h ist ein Teil des C99-Standards. Es definiert Formatbezeichner für printf/scanf wie zum Beispiel PRIu64 und es beinhaltet stdint.h das definiert typedefs wie uint32_t. Wenn Sie MSVC++/Windows verwenden, dann nehmen Sie inttypes.h aus code.google.com/p/msinttypes

    – jfs

    5. Mai 2010 um 16:33 Uhr

  • @Bei337: Es könnte helfen, wenn Sie den Code als C-Code kompilieren (nicht als C++). C++ erfordert __STDC_FORMAT_MACROS zu verwenden definiert werden PRI.. Makros.

    – jfs

    5. Mai 2010 um 20:52 Uhr

  • @PremalShah: uint32_t lo = (uint32_t)n, hi = (n >> 32);wo n ist ein unit64_t Variable.

    – jfs

    30. August 2012 um 14:16 Uhr

Benutzer-Avatar
zweiwk

long long val = (long long) mostSignificantWord << 32 | leastSignificantWord;
printf( "%lli", val );

  • << 32 wird nicht richtig funktionieren, wenn mostSignificantWord ist eine 32-Bit-Ganzzahl. Es muss zuerst in einen 64-Bit-Typ umgewandelt werden. Übrigens in g++ long und long long sind beide 8 Bytes.

    – Alex Korban

    4. Mai 2010 um 21:25 Uhr

  • @Alex: in g++ auf x64, long ist 64 Bit. Auf x86 sind es 32 Bit.

    – Steve Jessop

    4. Mai 2010 um 22:27 Uhr

  • Bitte verwenden Sie C99-Typen uint32_t und uint64_t. Deshalb sind sie da.

    – Norman Ramsey

    5. Mai 2010 um 0:09 Uhr

  • Die Eingänge sind unsignedalso sollte die Ausgabe nicht auch sein, wenn das OP nichts anderes sagt? long long für sich impliziert signed long long int.

    – Unterstrich_d

    1. Januar 2019 um 22:38 Uhr


meine aufnahme:

unsigned int low = <SOME-32-BIT-CONSTRANT>
unsigned int high = <SOME-32-BIT-CONSTANT>

unsigned long long data64;

data64 = (unsigned long long) high << 32 | low;

printf ("%llx\n", data64); /* hexadecimal output */
printf ("%lld\n", data64); /* decimal output */

Ein anderer Ansatz:

unsigned int low = <SOME-32-BIT-CONSTRANT>
unsigned int high = <SOME-32-BIT-CONSTANT>

unsigned long long data64;
unsigned char * ptr = (unsigned char *) &data;

memcpy (ptr+0, &low, 4);
memcpy (ptr+4, &high, 4);

printf ("%llx\n", data64); /* hexadecimal output */
printf ("%lld\n", data64); /* decimal output */

Beide Versionen funktionieren und haben eine ähnliche Leistung (der Compiler optimiert die memcpy weg).

Die zweite Version arbeitet nicht mit Big-Endian-Zielen, aber otoh, es nimmt dem Rätselraten, ob die Konstante 32 32 oder 32ull sein soll, ein Ende. Etwas, bei dem ich mir nie sicher bin, wenn ich Verschiebungen mit Konstanten größer als 31 sehe.

  • Es ist unerheblich, ob der Verschiebungsbetrag konstant ist 32 oder 32ULL, da beide den gleichen Wert haben und das ist alles, was für die Verschiebung verwendet wird. Solange der zu verschiebende Wert den Typ hat unsigned long longalles ist in Ordnung.

    – Café

    4. Mai 2010 um 22:00 Uhr

  • Warum überhaupt die nicht-portable Lösung zeigen? Jetzt wird es jemand kopieren, und später wird der Code in eine andere Anwendung verschoben, und irgendwann wird etwas kaputt gehen und es wird ewig dauern, ihn zu debuggen.

    – Adrian McCarthy

    4. Mai 2010 um 22:23 Uhr

  • Verwenden Sie die C99-Typen mit expliziten Größen. Deshalb sind sie da.

    – Norman Ramsey

    5. Mai 2010 um 0:10 Uhr

Benutzer-Avatar
caiohamamura

Es gibt eine andere Möglichkeit, Arrays und Zeiger zu verwenden:

#include <stdio.h>
#include <inttypes.h>

int main(void) {
    // Two uint32_t to one uint64_t
    uint32_t val1[2] = {1000, 90000};
    uint64_t *val1_u64_ptr = (uint64_t*)val1; //intermediate pointer cast to avoid Wstrict-aliasing warnings
    uint64_t val2 = *val1_u64_ptr;
    printf("val2: %" PRIu64 "\n", val2);
    // val2: 386547056641000


    // back to uint32_t array from uint64_t
    uint64_t val3 = 386547056641000ull;
    uint32_t *val4 = (uint32_t*)&val3;
    printf("val4: %" PRIu32 ", %" PRIu32 "\n", val4[0], val4[1]);
    // val4: 1000, 90000
    
    return 0;
}

Dieser Code ist für mich viel einfacher zu verstehen und zu lesen. Sie erstellen nur einen zusammenhängenden Raum im Speicher mit zwei 32-bit unsigned int und dann wird dieser selbe Speicherplatz als Single gelesen 64-bit unsigned int Wert und umgekehrt. Es sind keine Operationen beteiligt, nur der Speicher wird als unterschiedliche Typen gelesen.

BEARBEITEN

Ich habe vergessen zu erwähnen, dass dies großartig ist, wenn Sie bereits eine haben 64-bit array von irgendwo lesen dann könnte man alles problemlos nachlesen 32-bit array Paare:

#include <stdio.h>
#include <inttypes.h>

int main() {
    uint64_t array64[] = {
        386547056641000ull,
        93929935171414ull,
        186655006591110ull,
        73141496240875ull,
        161460097995400ull,
        351282298325439ull,
        97310615654411ull,
        104561732955680ull,
        383587691986172ull,
        386547056641000ull
    };

    int n_items = sizeof(array64) / sizeof(array64[0]);
    
    uint32_t* array32 = (uint32_t*)&array64;
    for (int ii = 0; ii < n_items * 2; ii += 2) {
        printf("[%" PRIu32 ", %" PRIu32 "]\n", array32[ii], array32[ii + 1]);
    }

    return 0;
}

Ausgabe:

[1000, 90000]
[3295375190, 21869]
[22874246, 43459]
[2498157291, 17029]
[3687404168, 37592]
[1218152895, 81789]
[3836596235, 22656]
[754134560, 24345]
[4162780412, 89310]
[1000, 90000]

Union-Struktur verwenden

Noch besser und lesbarer wäre es, eine Struct Union wie von https://stackoverflow.com/a/2810339/2548351 zu verwenden:

#include <stdio.h>
#include <inttypes.h>

typedef union {
  int64_t big;
  struct {
    int32_t x;
    int32_t y;
  };
} xy_t;

int main() {
    // initialize from 64-bit
    xy_t value = {386547056641000ull};
    printf("[%" PRIu32 ",%" PRIu32 "]\n", value.x, value.y);
    // [1000, 90000]
    
    // initialize as two 32-bit
    xy_t value2 = {.x = 1000, .y = 90000};
    printf("%" PRIu64, value.big);
    // 386547056641000

    return 0;
}

Anstatt zu versuchen, Dezimalzahlen zu drucken, drucke ich oft in Hex.

Daher …

printf ("0x%x%08x\n", upper32, lower32);

Alternativ können Sie je nach Architektur, Plattform und Compiler manchmal mit etwas davonkommen wie …

printf ("%lld\n", lower32, upper32);

oder

printf ("%lld\n", upper32, lower32);

Diese alternative Methode ist jedoch sehr maschinenabhängig (Endian-ness sowie 64 vs. 32 Bit, …) und wird im Allgemeinen nicht empfohlen.

Hoffe das hilft.

Benutzer-Avatar
Juan Melado

Dieser Code funktioniert, wenn sowohl “upper32” als auch “lower32” negativ sind:

data64 = ((LONGLONG)upper32<< 32) | ((LONGLONG)lower32& 0xffffffff);

Benutzer-Avatar
Wladimir Palant

Sie könnten dies tun, indem Sie die 32-Bit-Werte an die richtigen Stellen im Speicher schreiben:

unsigned long int data64;
data64=lowerword
*(&((unsigned int)data64)+1)=upperword;

Dies ist jedoch maschinenabhängig, zum Beispiel funktioniert es auf Big-Endian-Prozessoren nicht korrekt.

  • könnten Sie Ihre Antwort näher erläutern und erklären

    – Taube

    19. Oktober 2012 um 11:04 Uhr

  • Dies funktioniert nur auf Little-Endian-Maschinen. Eine weitere Annahme ist, dass unsigned long 64 Bit und unsigned int 32 Bit beträgt (kann uint32 und uint64 verwenden). jetzt die Erklärung data64=lowerword dies wird das untere Wort in die unteren Bytes von data64 kopieren. (unsigned int) data64 konvertiert es in 32 Bit. &((unsigned int)data64) gibt mir die Adresse vom Typ unsigned int das Hinzufügen von 1 wird die Adresse zu den höheren Bytes von data64 inkrementieren schließlich * davon speichert das obere Wort in diesen höheren Bytes.

    – mohit

    20. Oktober 2012 um 9:30 Uhr


  • Dies hat ein undefiniertes Verhalten; es verstößt gegen striktes Aliasing. Verwenden memcpy wenn Sie dies tun möchten, oder besser gar nicht. Compiler verstehen ((uint64_t)a<<32) | b

    – Peter Cordes

    5. Juni 2019 um 2:00 Uhr

1369990cookie-checkWie kombiniert man zwei 32-Bit-Ganzzahlen zu einer 64-Bit-Ganzzahl?

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

Privacy policy