这种按位转换安全吗?

问题描述 投票:0回答:1

我遇到一种情况,我需要将 16 位打包成 64 位数字,然后将它们作为 [ -32768, 32768 ) 范围内的有符号整数读回。我为此选择的方法是将数字计算为有符号 16 位 int,立即将其转换为无符号 16 位 int,然后将其向上转换为 64 位无符号 int,然后执行适当的位移以获得将关键的 16 位放入正确的位置。

这是创建位打包排列的伪代码:

Given int x, y such that x - y >= -32768 and y - x < 32768;
const int MASK_POS = 45;
const unsigned short int u_s = x - y;
unsigned long long int ull_s = u_s;
ull_s <<= MASK_POS;

这是提取原始数字差异的伪代码:

Given unsigned long long int ull_s with 16 bits encoding a signed integer in the 46th through 61st bits;
const unsigned short int u_s = ((ulls >> MASK_POS) & 0xffff);
const short int s_s = u_s;
const int difference_x_and_y = s_s;

在我看来,这似乎是打包有符号整数并提取它的合理方法。在对负整数执行位移位时,我对特定于平台的行为持谨慎态度,但我认为在升级数字中的总位数之前转换为相同位数的无符号形式,并反向提取无符号在转换为相同大小的有符号整数之前,所需位长度的整数将是安全的。

(如果有人好奇的话,这个 64 位无符号整数的其他 48 位中将会发生很多事情——从高 3 位到低 31 和中间 14,一切都有已经解析出来了。我当然可以编写一些单元测试来确保这种行为在任何架构上都适用,但是如果有人现在可以看到缺陷,最好提前知道。谢谢!)

c++ bit-manipulation signed unsigned-integer
1个回答
1
投票

你所做的一切都很好。从 C++20 开始,有符号整数需要具有二进制补码表示形式,并且所有有符号/无符号转换都是明确定义的,相当于

std::bit_cast
。即使在此之前,您关心的任何实现都会以这种方式运行。

但是,如果您使用像

std::uint16_t
这样的固定宽度类型可能会更好,因为您的代码严重依赖于特定宽度。

struct quad {
    std::int16_t x, y, z, w;
};

inline std::uint64_t pack(quad q) {
    return std::uint64_t{q.x} <<  0
         | std::uint64_t{q.y} << 16
         | std::uint64_t{q.z} << 32
         | std::uint64_t{q.w} << 48;
}

inline quad unpack(std::uint64_t x) {
    // just let implicit conversions do their thing
    return { x >> 0, x >> 16, x >> 32, x >> 48 };
}

您可以像这样打包整数,但这引出了一个问题,为什么您不能直接使用像

struct
这样的
quad
。 任何理智的编译器都不会向
quad
添加填充,您可以使用

来确保它
static_assert(sizeof(quad) == sizeof(std::uint64_t));

编译器也不允许对

quad
的成员重新排序,因此出于所有意图和目的,您可以将整数捆绑在
quad
中,而不是将它们打包成整数。

© www.soinside.com 2019 - 2024. All rights reserved.