结构中的位顺序不是我所期望的
Bit order in struct is not what I would have expected
我有一个使用 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;
}
在示例中,我希望尾数为 0x00000054,指数为 0x0000001F,符号为 0x00000001
相反,我得到尾数:0x0000013f,指数:0x00000005,符号:0x00000000
这意味着首先从我的位掩码中获取符号(第一位),接下来是指数的 5 位,然后是尾数的 10 位,所以顺序与我想要的相反。为什么会这样?
更糟糕的是,不同的编译器可能会给出预期的顺序。该标准从未指定位域的实现细节,尤其是顺序。理由和往常一样,它是一个实现细节,程序员不应该依赖也不依赖于它。
缺点是不能在跨语言程序中使用位域,并且程序员不能使用位域来处理具有众所周知的位域的数据(例如在网络协议头中),因为它太复杂而无法确保实施将如何处理它们。
出于这个原因,我一直认为这只是一个无法使用的功能,我只在无符号类型而不是位域上使用位掩码。但最后一部分只是我自己的意见......
我会说你的 input
是不正确的,无论如何对于这个编译器。这就是 float16_and_int16
订单的样子。
sign exponent mantissa
[15] [14:10] [9:0]
或
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 |
如果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
所以
MANTISSA == 0100111111 (0x13F)
EXPONENT == 00101 (0x5)
SIGN == 0 (0x0)
如果您希望尾数为 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
或
input = 0xFC64
我有一个使用 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;
}
在示例中,我希望尾数为 0x00000054,指数为 0x0000001F,符号为 0x00000001
相反,我得到尾数:0x0000013f,指数:0x00000005,符号:0x00000000
这意味着首先从我的位掩码中获取符号(第一位),接下来是指数的 5 位,然后是尾数的 10 位,所以顺序与我想要的相反。为什么会这样?
更糟糕的是,不同的编译器可能会给出预期的顺序。该标准从未指定位域的实现细节,尤其是顺序。理由和往常一样,它是一个实现细节,程序员不应该依赖也不依赖于它。
缺点是不能在跨语言程序中使用位域,并且程序员不能使用位域来处理具有众所周知的位域的数据(例如在网络协议头中),因为它太复杂而无法确保实施将如何处理它们。
出于这个原因,我一直认为这只是一个无法使用的功能,我只在无符号类型而不是位域上使用位掩码。但最后一部分只是我自己的意见......
我会说你的 input
是不正确的,无论如何对于这个编译器。这就是 float16_and_int16
订单的样子。
sign exponent mantissa
[15] [14:10] [9:0]
或
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 |
如果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
所以
MANTISSA == 0100111111 (0x13F)
EXPONENT == 00101 (0x5)
SIGN == 0 (0x0)
如果您希望尾数为 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
或
input = 0xFC64