将一种类型的位重新解释为另一种类型的技术

时间:2018-08-03 01:40:33

标签: c++ pointers casting

注意:这个问题始于一个错误的前提:看起来为0.0的值实际上是非常小的数字。但是它变成了关于将一种类型的位重新解释为另一种类型的不同方式的讨论。 TL; DR:在C ++ 20推出新的bit_cast类之前,标准的便携式解决方案是memcpy

更新3:这是一个演示问题的非常简短的应用程序。解释为float的位的值始终为0.0000:

#include <iostream>
int main() {

    uint32_t i = 0x04501234;  // arbitrary bits should be a valid float
    uint32_t bitPattern;
    uint32_t* ptr_to_uint = &bitPattern;

    volatile float bitPatternAsFloat;
    volatile float* ptr_to_float = &bitPatternAsFloat;

    do {
        bitPattern = i;

        memcpy( (void*)ptr_to_float, (const void*)ptr_to_uint, 4 );

        // The following 2 lines both print the float value as 0.00000
        //printf( "bitPattern: %0X, bitPatternAsFloat: %0X, as float: %f \r\n", bitPattern, *(unsigned int*)&bitPatternAsFloat, bitPatternAsFloat );
        printf( "bitPattern: %0X, bitPatternAsFloat: %0X, as float: %f \r\n", bitPattern, *(unsigned int*)&bitPatternAsFloat, *ptr_to_float );

        i++;

    } while( i < 0x04501254 );

    return 0;
}

(原始帖子)

float bitPatternAsFloat; 
for (uint32_t i = 0; i <= 0xFFFFFFFF; i = (i & 0x7F800000) == 0 ? i | 0x00800000 : i + 1 ) 
    {
        bitPatternAsFloat = *(float*)&i;
...

循环将遍历每个位模式,跳过float指数字段为0的位模式。然后,我尝试将这些位解释为floati的值看起来不错(以十六进制打印),但是bitPatternAsFloat总是返回0.0。怎么了?

考虑到可能i保留在寄存器中,因此&i不返回任何内容,我尝试先将i复制到另一个uint32_t变量,但是结果是相同的。

我知道我正在for语句中用三进制修改循环变量,但是据我所知这是可以的。

更新:在阅读了有关别名的信息后,我尝试了此操作:

union FloatBits { 
    uint32_t uintVersion;
    float floatVersion;
};
float bitPatternAsFloat;

// inside test method: 
union FloatBits fb;  // also tried it without the 'union' keyword

// inside test loop (i is a uint32_t incrementing through all values)
fb.uintVersion = i;
bitPatternAsFloat = fb.floatVersion;

当我打印值时,fb.uintVersion打印期望值i,但是fb.floatVersion仍然打印为0.000000。我靠近吗?我想念什么?编译器为g++ 6,它允许“类型校正”。

更新2:这是我尝试使用memcpy的尝试。它不起作用(浮点值始终为0.0):

uint32_t i = 0;
uint32_t bitPattern;
float bitPatternAsFloat;

uint32_t* ptr_to_uint  = &bitPattern;
float*    ptr_to_float = &bitPatternAsFloat;

do {
    bitPattern = i; //incremented from 0x00000000 to 0xFFFFFFFF
    memcpy( (void*)ptr_to_float, (const void*)ptr_to_uint, 4 );
    // bitPatternAsFloat always reads as 0.0
    ...
    i++;
    } while( i );

1 个答案:

答案 0 :(得分:0)

感谢@geza指出“失败”是printf的伪像,无法显示%f格式的非常小的数字。使用%e格式会显示正确的值。

在我的测试中,讨论的所有各种转换方法都起作用,甚至是未定义的行为。考虑到简单性,性能和可移植性,使用union的解决方案似乎是最好的:

uint32_t bitPattern; 
volatile float bitPatternAsFloat;

union UintFloat {
    uint32_t asUint;
    float asFloat;
} uintFloat;

do {
    bitPattern = i;  // i is loop index
    uintFloat.asUint = bitPattern;                               
    bitPatternAsFloat = uintFloat.asFloat;

一个有趣的问题是,float中的union成员是否应声明为volatile:它从未显式写入,因此编译器可能会缓存初始值并重用它,从而失败注意到相应的uint32_t正在更新?在没有volatile声明的情况下,它在我当前的编译器上似乎可以正常工作,但这是否是潜在的“陷阱”?

P.S。我刚刚发现[reinterpret_cast][1],想知道这是否是最佳解决方案。