C++ struct中的位顺序不是我所期望的

C++ struct中的位顺序不是我所期望的,c++,endianness,bit-fields,C++,Endianness,Bit Fields,我有一个使用16位浮点的框架,我想分离它的组件,然后用于32位浮点。在我的第一种方法中,我使用了位移位和类似的方法,虽然有效,但阅读起来却非常混乱 然后我想改用自定义的位大小的结构,并使用一个并集来写入该结构 重现问题的代码: #include <iostream> #include <stdint.h> union float16_and_int16 { struct { uint16_t Mantissa : 10;

我有一个使用16位浮点的框架,我想分离它的组件,然后用于32位浮点。在我的第一种方法中,我使用了位移位和类似的方法,虽然有效,但阅读起来却非常混乱

然后我想改用自定义的位大小的结构,并使用一个并集来写入该结构

重现问题的代码:

#include <iostream>
#include <stdint.h>

union float16_and_int16
{
    struct
    {
        uint16_t    Mantissa : 10;
        uint16_t    Exponent : 5;
        uint16_t    Sign : 1;
    } Components;

    uint16_t bitMask;
};

int main()
{
    uint16_t input = 0x153F;

    float16_and_int16 result;
    result.bitMask = input;

    printf("Mantissa: %#010x\n", result.Components.Mantissa);
    printf("Exponent: %#010x\n", result.Components.Exponent);
    printf("Sign:     %#010x\n", result.Components.Sign);
    return 0;
}
#包括
#包括
联合浮动16_和_int16
{
结构
{
uint16尾数:10;
uint16_t指数:5;
uint16_t符号:1;
}组成部分;
uint16位掩码;
};
int main()
{
uint16_t输入=0x153F;
浮点16_和浮点16结果;
result.bitMask=输入;
printf(“尾数:%#010x\n”,result.Components.尾数);
printf(“指数:%#010x\n”,result.Components.Exponent);
printf(“符号:%#010x\n”,result.Components.Sign);
返回0;
}
在本例中,我希望尾数为0x00000054,指数为0x0000001F,符号为0x00000001

相反,我得到尾数:0x000001f,指数:0x00000005,符号:0x00000000


这意味着从我的位掩码开始,首先取符号(第一位),然后5位取指数,然后10位取尾数,所以顺序与我想要的相反。为什么会发生这种情况?

更糟糕的是,不同的编译器可能会给出预期的顺序。该标准从未指定位字段的实现细节,特别是顺序。其基本原理通常是,这是一个实现细节,程序员不应该依赖它

缺点是不可能在跨语言程序中使用位字段,程序员不能使用位字段处理具有已知位字段的数据(例如在网络协议头中),因为它太复杂,无法确保实现将如何处理它们


出于这个原因,我一直认为这只是一个不可用的特性,我只在无符号类型上使用位掩码而不是位字段。但最后一部分只不过是我自己的观点…

我想说,对于这个编译器来说,您的
输入是不正确的。这就是
float16\u和\u int16
顺序的外观

 sign   exponent  mantissa
 [15]   [14:10]    [9:0]

如果
input=0x153F
bitMask==

SGN |  E  X  P  O  N  E  N  T|     M  A   N   T   I   S   S   A                |
 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
 0     0    0    1    0    1    0    1    0    0    1    1    1    1    1    1
所以

如果希望尾数为0x54,则需要指数0x1f和符号0x1

SGN |  E  X  P  O  N  E  N  T|     M  A   N   T   I   S   S   A                |
 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
 1     1    1    1    1    1    0    0    0    1    0    1    0    1    0    0


注意:它甚至看起来不像endianess或其他什么,整个结构的顺序都是错误的,不管什么原因。注意:从联合中活动字段以外的任何字段读取都是形式上未定义的行为。你的编译器可以使它合法(默认或通过开关),但是通过联盟进行类型惩罚不是标准兼容C++。gotcha@SinisterMJ
是的,很遗憾。看来标准化小组忘记了一种语言必须用于现实世界的程序
。语言是这样的,我们必须接受它……因为它“不”太复杂,不太复杂——是不是“不”的意思?@Ajay:当然不是!这是一个打字错误:-(.谢谢你的注意是的,我注意到了,问题是,为什么位域的顺序是符号->指数->尾数。我定义它的顺序与此相反。当你在一个联合中定义struct为位域时,定义的第一个是最不重要的。这很有意义,如果你只有几个位,它们将与fil的右边对齐l最低有效位
MANTISSA == 0100111111  (0x13F)
EXPONENT == 00101 (0x5)
SIGN == 0 (0x0)
SGN |  E  X  P  O  N  E  N  T|     M  A   N   T   I   S   S   A                |
 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
 1     1    1    1    1    1    0    0    0    1    0    1    0    1    0    0
input = 0xFC64