dsc*_*tti 7 c++ signed bit-manipulation unsigned-integer
我遇到一种情况,需要将 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;
Run Code Online (Sandbox Code Playgroud)
这是提取原始数字差异的伪代码:
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;
Run Code Online (Sandbox Code Playgroud)
在我看来,这似乎是打包有符号整数并提取它的合理方法。在对负整数执行位移位时,我对特定于平台的行为持谨慎态度,但我认为在升级数字中的总位数之前转换为相同位数的无符号形式,并反向提取无符号在转换为相同大小的有符号整数之前,所需位长度的整数将是安全的。
(如果有人好奇的话,这个 64 位无符号整数的其他 48 位中将会发生很多事情——从高 3 位到低 31 和中间 14,一切都已经我当然可以编写一些单元测试来确保这种行为适用于任何架构,但如果有人现在可以看到缺陷,最好提前知道。)
你正在做的事情完全没问题。从 C++20 开始,有符号整数需要具有二进制补码表示形式,并且所有有符号/无符号转换都是明确定义的,并且等效于std::bit_cast. 甚至在此之前,您关心的任何实现都会以这种方式运行。
但是,如果您使用固定宽度类型可能会更好,std::uint16_t因为您的代码严重依赖于特定宽度。
struct quad {
std::int16_t x, y, z, w;
};
inline std::uint64_t pack(quad q) {
// Two-step conversion to std::uint16_t -> std::uint64_t
// to avoid a sign extension when going directly to std::uint64_t.
// Alternatively, mask each operand with 0xffff.
return std::uint64_t{std::uint16_t(q.x)} << 0
| std::uint64_t{std::uint16_t(q.y)} << 16
| std::uint64_t{std::uint16_t(q.z)} << 32
| std::uint64_t{std::uint16_t(q.w)} << 48;
// alternatively, if you don't care about relying on
// platform endianness ...
return std::bit_cast<std::uint64_t>(q); // note: only works if quad is unpadded
}
inline quad unpack(std::uint64_t x) {
// just let implicit conversions do their thing
return { x >> 0, x >> 16, x >> 32, x >> 48 };
// once again, alternatively ...
return std::bit_cast<quad>(x);
}
Run Code Online (Sandbox Code Playgroud)
您可以像这样打包整数,但这引出了一个问题,为什么您不能直接使用structlike quad。没有理智的编译器会添加填充到quad,你可以用以下命令来确保它
static_assert(sizeof(quad) == sizeof(std::uint64_t));
Run Code Online (Sandbox Code Playgroud)
编译器也不允许对 的成员重新排序quad,因此出于所有意图和目的,您可以将整数捆绑在一起,quad而不是将它们打包成整数。