C - 将两个 uint32_t 组合成一个 double

C - combine two uint32_t to a double

您好,我正在研究两种协议之间的套接字转换。我从一个二进制文件中读取并将解析后的 header 存储到一个 uint32_t 类型的数组中。然后我从数组中取出字段并将它们转换为相应的类型。到目前为止 uint32_t/int32_t/uint16_tint32_t 工作正常。

但是,当我尝试合并两个 uint32_t(一个接一个地附加)然后将这个 64 位长数据转换为双精度数据时,我得到了各种错误的输出。

作为 C 编程的新手,我正在努力学习 double / float 表示的计算机方法。

基本上我想做的是:在不改变两个uint32_t的位模式的情况下,concast一个接一个地拼接成一个64位的数据,然后将数据转换为 double。最重要的是不要改变位模式,因为那部分位流应该是 double.

以下为部分代码:

uint32_t* buffer = (uint32_t*) malloc (arraySize * sizeof(uint32_t));

...

double outputSampleRate = ((union { double i; double outputSampleRate; })
    { .i = ((uint64_t)buffer[6] << 32 | (uint64_t)buffer[7])}).outputSampleRate;

输入文件中的数据:

35.5

我的代码后的值:

4630192998146113536.000000

另外,有没有更好的方法来处理套接字header解析?

您的联合定义不正确,您希望 i 定义为 uint64_t:

double outputSampleRate = ((union { uint64_t i; double d; })
    { .i = ((uint64_t)buffer[6] << 32 | (uint64_t)buffer[7])}).d;

您也可能 运行 遇到字节顺序问题。试试小端:

double outputSampleRate = ((union { unt64_t i; double d; })
    { .i = ((uint64_t)buffer[7] << 32) | (uint64_t)buffer[6]}).d;

通过 union 重新解释表示的位实际上得到了 C 标准的支持,称为类型双关。如果这些位表示目标类型的陷阱值,则不能保证工作。

您可以尝试其他转换和技巧:试试您的运气并使用指针转换:

double outputSampleRate = *(uint64_t*)&buffer[6];

另一种强制类型双关的方法是使用 memcpy 函数:

double outputSampleRate;
uint64_t temp = ((uint64_t)buffer[7] << 32) | (uint64_t)buffer[6];
memcpy(&outputSampleRate, &temp, sizeof(outputSampleRate));

或者简单地说:

double outputSampleRate;
memcpy(&outputSampleRate, &buffer[6], sizeof(outputSampleRate));

但它似乎也不能保证有效,尽管我在生产代码中看到了上述两种情况的一些实例。

通过联合重新解释位模式需要联合元素具有正确的类型。你的 union 有两个 doubles,所以当你从一个读取时,它与另一个具有相同的值。从 uint32_tdouble 的转换将保留数值结果,解释 "garbage",这实际上只是将 double 重新解释为整数。您还需要使用正确的字节顺序(低字在前?高字在前?),最简单的方法是完全避免位移。

double outputSampleRate = ((union { uint32_t i[2]; double d; })
    { .i = { buffer[6], buffer[7] } }).d;

您可以使用 uint64_t i 但是...何必呢?

您也可以使用 memcpy() 复制字节...

double outputSampleRate;
memcpy(&outputSampleRate, &buffer[6], sizeof(outputSampleRate));

通常的注意事项适用:虽然这些解决方案相对可移植,但它们没有考虑字节序问题,并且它们不会在违反您的假设的系统上运行,例如double 有多大,但做出这些假设通常是安全的。