C语言 如何将两个32位整数合并为一个64位整数?

1sbrub3j  于 2022-12-11  发布在  其他
关注(0)|答案(8)|浏览(544)

我有一个计数寄存器,它由两个32位无符号整数组成,一个用于值的高32位(最高有效字),另一个用于值的低32位(最低有效字)。
在C语言中,将这两个32位无符号整数组合起来,然后显示为一个大数的最佳方法是什么?
具体而言:

leastSignificantWord = 4294967295; //2^32-1

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

这会打印得很好。
当数字增加到4294967296时,我有了它,所以leastSignificantWord擦除到0,mostSignificantWord(最初为0)现在为1。整个计数器现在应该为4294967296,但现在它只为10,因为我只是将mostSignificantWord中的1和leastSignificantWord中的0连接起来。
我应该如何让它显示4294967296而不是10?

idfiyjo8

idfiyjo81#

在这种情况下,使用具有 explicit 大小的 unsigned 整数可能比较有利:

#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;
}

输出
4294967296

分解(uint64_t) mostSignificantWord << 32 | leastSignificantWord

  • (typename)在C中执行typecasting。它将值数据类型更改为typename

(uint64_t)0x 00000001-〉0x 0000000000000000001的数据类型和数据类型

  • <<执行左移。在C中,对无符号整数执行左移执行logical shift

0x 00000000000001〈〈32 -〉0x 0000000001000000000的最大值,则为零。

  • |执行“按位或”(对操作数的位执行逻辑或)。

0 b 0101中的值|0 b1001-〉0 b1101之间的关系

mf98qq94

mf98qq942#

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

wqlqzqxt3#

我看法:

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 */

另一种方法:

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 */

这两个版本都可以工作,并且它们将具有类似的性能(编译器将优化memcpy)。
第二个版本不适用于big-endian目标,但它消除了猜测常数32应该是32还是32 ull的工作。当我看到常数大于31时,我从来不确定。

eiee3dmh

eiee3dmh4#

还有另一种使用数组和指针的方法:

#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;
}

这段代码对我来说更容易理解和阅读。你只是在内存中创建了一个连续的空间,然后这个内存空间被读取为一个64-bit unsigned int值,反之亦然。没有任何操作只涉及内存被读取为不同的类型。

编辑

忘了提一下,如果你已经从某个地方读取了64-bit array,那么你可以很容易地将所有内容读为32-bit array对,这是很棒的:

#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;
}

输出量:

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

使用联合结构

使用https://stackoverflow.com/a/2810339/2548351中的结构联合更好、更易读:

#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;
}
wdebmtf2

wdebmtf25#

我经常以十六进制打印,而不是尝试打印十进制。
所以......

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

或者,根据体系结构、平台和编译器的不同,有时您可以使用类似于...

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

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

但是,这种替代方法非常依赖于计算机(字节序,以及64位与32位,...),通常不建议使用。
希望这对你有帮助。

xqnpmsa8

xqnpmsa86#

当upper32和lower32都为负数时,此代码有效:

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

0aydgbwb7#

您可以通过将32位值写入内存中的正确位置来完成此操作:

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

但是,这取决于计算机,例如,它在big-endian处理器上无法正常工作。

jgovgodb

jgovgodb8#

在游戏后期,但我需要这样一个东西类似于表示一个64位整数的数字base 10字符串在一个32位嵌入式env。
因此,受Link的启发,我编写了这段代码,它可以完成问题中提出的任务,但不限于base 10:可以转换成2 - 10的任何一个碱基,并且可以容易地扩展到N碱基。

void stringBaseAdd(char *buf, unsigned long add, int base){

    char tmp[65], *p, *q;
    int l=strlen(buf);
    int da1, da2, dar;
    int r;

    tmp[64]=0;
    q=&tmp[64];
    p=&buf[l-1];
    r=0;
    while(add && p>=buf){

        da1=add%base;
        add/=base;
        da2=*p-'0';
        dar=da1+da2+r;

        r=(dar>=base)? dar/base: 0;
        *p='0'+(dar%base);
        --p;
    }

    while(add){

        da1=add%base;
        add/=base;
        dar=da1+r;

        r=(dar>=base)? dar/base: 0;
        --q;
        *q='0'+(dar%base);
    }

    while(p>=buf && r){

        da2=*p-'0';
        dar=da2+r;

        r=(dar>=base)? 1: 0;
        *p='0'+(dar%base);
        --p;
    }

    if(r){

        --q;
        *q='0'+r;
    }

    l=strlen(q);
    if(l){

        memmove(&buf[l], buf, strlen(buf)+1);
        memcpy(buf, q, l);
    }
}
void stringBaseDouble(char *buf, int base){

    char *p;
    int l=strlen(buf);
    int da1, dar;
    int r;

    p=&buf[l-1];
    r=0;
    while(p>=buf){

        da1=*p-'0';
        dar=(da1<<1)+r;

        r=(dar>=base)? 1: 0;
        *p='0'+(dar%base);
        --p;
    }

    if(r){

        memmove(&buf[1], buf, strlen(buf)+1);
        *buf='1';
    }
}
void stringBaseInc(char *buf, int base){

    char *p;
    int l=strlen(buf);
    int da1, dar;
    int r;

    p=&buf[l-1];
    r=1;
    while(p>=buf && r){

        da1=*p-'0';
        dar=da1+r;

        r=(dar>=base)? 1: 0;
        *p='0'+(dar%base);
        --p;
    }

    if(r){

        memmove(&buf[1], buf, strlen(buf)+1);
        *buf='1';
    }
}
void stringLongLongInt(char *buf, unsigned long h, unsigned long l, int base){

    unsigned long init=l;
    int s=0, comb=0;

    if(h){

        comb=1;
        init=h;
        while(!(init&0x80000000L)){

            init<<=1;
            init|=(l&0x80000000L)? 1: 0;
            l<<=1;
            s++;
        }
    }

    buf[0]='0';
    buf[1]=0;
    stringBaseAdd(buf, init, base);

    if(comb){

        l>>=s;
        h=0x80000000L>>s;
        s=sizeof(l)*8-s;
        while(s--){

            stringBaseDouble(buf, base);
            if(l&h)
                stringBaseInc(buf, base);

            h>>=1;
        }
    }
}

如果您要求

char buff[20];
stringLongLongInt(buff, 1, 0, 10);

您缓冲区将包含4294967296

相关问题