【问题标题】:Bit order in struct is not what I would have expected结构中的位顺序不是我所期望的
【发布时间】:2019-06-04 07:24:54
【问题描述】:

我有一个使用 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 位到尾数,所以顺序与我想要的相反。为什么会这样?

【问题讨论】:

  • 注意:它甚至看起来不像是字节序之类的东西,无论出于何种原因,整个结构的顺序都是错误的
  • 注意:从联合中活动字段以外的任何字段读取正式未定义行为。您的编译器可能使其合法(默认情况下或通过开关),但通过联合的类型双关语不是符合标准的 C++。
  • Bitfields and alignment的可能重复

标签: c++ endianness bit-fields


【解决方案1】:

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

缺点是不能在跨语言程序中使用位域,并且程序员不能使用位域来处理具有众所周知的位域(例如在网络协议标头中)的数据,因为它太复杂,无法确定如何使用实现将处理它们。

出于这个原因,我一直认为它只是一个无法使用的功能,并且我只在无符号类型而不是位域上使用位掩码。但最后一部分只是我个人的看法......

【讨论】:

  • 所以回到位移,而不是让它更好的可读性......明白了
  • @SinisterMJ &lt;rank&gt; 是的,很遗憾。看起来标准化小组已经忘记了一种语言必须用于现实世界的程序&lt;/rant&gt;。语言就是这样,我们必须接受它......
  • 因为它“不是”太复杂而无法制作 - 是不是故意的?
  • @Ajay:当然不是!这是一个错字:-(。感谢您的注意
【解决方案2】:

我会说你的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

【讨论】:

  • 是的,我注意到了,重点是,为什么位域符号中的顺序是 -> 指数 -> 尾数。我以相反的顺序定义它。
  • 当您将联合中的结构定义为位域时,定义的第一个是最不重要的。这种是有道理的,如果你只有几个位,它们将向右对齐以填充最低有效位
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-02-02
  • 1970-01-01
  • 1970-01-01
  • 2021-09-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多