将两个短裤组合成int的最简洁方法

时间:2009-08-18 15:30:05

标签: c++

我有两个16位短路(s1和s2),我正在尝试将它们组合成一个32位整数(i1)。根据我正在处理的规范,s1是最重要的单词,而s2是最不重要的单词,并且组合单词似乎是签名的。 (即s1的最高位是符号。)

组合s1和s2的最简洁方法是什么?

我想像

const utils::int32 i1 = ((s1<<16) | (s2));

会这样做,而且似乎有效,但我担心左移16分。

另外,我对使用工会做这项工作的想法很感兴趣,对这是一个好主意还是坏主意有任何想法?

7 个答案:

答案 0 :(得分:13)

如果short和int都是无符号的,那么你所做的只有意义。如果其中一个短路是有符号且具有负值,那么将它们组合成单个int的想法是没有意义的,除非您已经提供了特定于域的规范来涵盖这种可能性。

答案 1 :(得分:6)

你所看到的几乎是正确的,但如果第二部分是否定的话可能会失败;隐式转换为int可能会签名扩展并用1填充高16位。对无符号短路的转换可能会阻止这种情况发生,但最好的方法是屏蔽掉这些位。

const utils::int32 combineddata = ((data.first<<16) | ((data.second) & 0xffff));

答案 2 :(得分:5)

我知道这是一个很老的帖子,但目前发布的答案的质量令人沮丧......

这些是需要考虑的问题:

  • short(或其他小整数类型)的隐式整数提升将导致类型int的操作数被签名。无论小整数类型的签名如何,都会发生这种情况。整数提升发生在班次操作和按位OR中。
  • 对于移位运算符,结果类型是提升左操作数的类型。在按位OR的情况下,结果类型从“通常的算术转换”获得。
  • 左移一个负数导致未定义的行为。右移负数导致实现定义的行为(逻辑与算术移位)。因此,在99%的用例中,不应将带符号的数字与位移一起使用。
  • 工会,数组和类似工具都是糟糕的解决方案,因为它们会使代码依赖于endianess。此外,通过联合打字也不是C ++中明确定义的行为(与C不同)。基于指针的解决方案很糟糕,因为它们最终会违反“严格的别名规则”。

因此,正确的解决方案是:

  • 使用保证未签名且不会隐式提升的类型的操作数。
  • 使用位移,因为这些是与endianess无关的。
  • 不使用一些带有联合或指针的非便携式hogwash解决方案。除了不可移植性之外,这些解决方案绝对没有任何好处。但是,此类解决方案可能会引发一个或多个未定义行为的情况。

看起来像这样:

int32_t  i32 = (int32_t)( (uint32_t)s1<<16 | (uint32_t)s2 );

任何其他解决方案都非常值得怀疑,最多也是非便携式的。

答案 3 :(得分:3)

由于没人发布,这就是联盟的样子。但关于endian-ness的评论肯定适用。

大端:

typedef union {
    struct {
        uint16_t high;
        uint16_t low;
    } pieces;
    uint32_t all;
} splitint_t;

小端:

typedef union {
    struct {
        uint16_t low;
        uint16_t high;
    } pieces;
    uint32_t all;
} splitint_t;

答案 4 :(得分:1)

尝试将data.second explicite投影到短类型,例如:

const utils::int32 combineddata = ((data.first<<16) | ((short)data.second));

编辑:我是C#dev,可能你的代码语言中的转换看起来不一样,但想法可能是相同的。

答案 5 :(得分:0)

您希望在执行转换之前将data.first转换为int32,否则转移将在存储被分配到组合数据时自动升级之前溢出存储。

尝试:

const utils::int32 combineddata = (static_cast<utils::int32>(data.first) << 16) | data.second;

这当然假设data.first和data.second是保证精确16位长的类型,否则你会遇到更大的问题。

我真的不明白你的陈述“如果data.second变得太大,|将不会考虑到它们都是短路的事实。”

编辑: Neil对签名绝对正确。

答案 6 :(得分:-1)

使用union来完成这项工作看起来是个不错的选择,但由于处理器的字节差异而导致可移植性问题。它是可行的,但您需要准备好根据目标体系结构修改联合。位移是可移植的,但请编写一个函数/方法来为您完成。如果你愿意,可以内联。

关于短裤的签名,对于这种类型的操作,重要的不是数据类型。换句话说,如果s1和s2意味着被解释为32位字的两半,那么只有当你做一些会导致s2被符号扩展的东西时才设置第15位。请参阅Mark Ransoms答案,这可能会更好

inline utils::int32 CombineWord16toLong32(utils::int16 s1, utils::int16 s2)
{
    return ((s1 <<16) | (s2 & 0xffff));
}