简体   繁体   English

将有符号 32 位存储在无符号 64 位 int 中

[英]Store signed 32-bit in unsigned 64-bit int

Basically, what I want is to "store" a signed 32-bit int inside (in the 32 rightmost bits) an unsigned 64-bit int - since I want to use the leftmost 32 bits for other purposes.基本上,我想要的是在(最右边的 32 位)一个无符号的 64 位 int 中“存储”一个有符号的 32 位 int - 因为我想将最左边的 32 位用于其他目的。

What I'm doing right now is a simple cast and mask:我现在正在做的是一个简单的演员和面具:

#define packInt32(X) ((uint64_t)X | INT_MASK)

But this approach has an obvious issue: If X is a positive int (the first bit is not set), everything goes fine.但是这种方法有一个明显的问题:如果X是正整数(第一位未设置),则一切正常。 If it's negative, it becomes messy.如果它是负数,它就会变得凌乱。


The question is:问题是:

How to achieve the above, also supporting negative numbers, in the fastest and most-efficient way ?如何以最快和最有效的方式实现上述目标,同时支持负数?

The "mess" you mention happens because you cast a small signed type to a large unsigned type.你提到的“混乱”发生是因为你将一个小的有符号类型转换为一个大的无符号类型。 During this conversion the size is adjusted first with applying sign extension.在此转换期间,首先通过应用符号扩展来调整大小。 This is what causes your trouble.这就是导致您遇到麻烦的原因。

You can simply cast the (signed) integer to an unsigned type of same size first.您可以先将(有符号)整数简单地转换为相同大小的无符号类型。 Then casting to 64 bit will not trigger sign extension:然后转换为 64 位不会触发符号扩展:

#define packInt32(X) ((uint64_t)(uint32_t)(X) | INT_MASK)

You need to mask out any bits besides the low order 32 bits.您需要屏蔽除低位 32 位之外的任何位。 You can do that with a bitwise AND:你可以用按位 AND 来做到这一点:

#define packInt32(X) (((uint64_t)(X) & 0xFFFFFFFF) | INT_MASK)

A negative 32-bit integer will get sign-extended into 64-bits.负的 32 位整数将被符号扩展为 64 位。

#include <stdint.h>
uint64_t movsx(int32_t X) { return X; }

movsx on x86-64: x86-64 上的 movsx:

movsx:
        movsx   rax, edi
        ret

Masking out the higher 32-bits will remove cause it to be just zero-extended:屏蔽较高的 32 位将导致它只是零扩展:

#include <stdint.h>
uint64_t mov(int32_t X) { return (uint64_t)X & 0xFFFFFFFF; }
//or uint64_t mov(int32_t X) { return (uint64_t)(uint32_t)X; }

mov on x86-64:在 x86-64 上移动:

mov:
        mov     eax, edi
        ret

https://gcc.godbolt.org/z/fihCmt https://gcc.godbolt.org/z/fihCmt

Neither method loses any info from the lower 32-bits, so either method is a valid way of storing a 32-bit integer into a 64-bit one.这两种方法都不会丢失低 32 位的任何信息,因此任一方法都是将 32 位整数存储为 64 位整数的有效方法。

The x86-64 code for a plain mov is one byte shorter (3 bytes vs 4).普通mov的 x86-64 代码短一个字节(3 个字节对 4 个字节)。 I don't think there should be much of a speed difference, but if there is one, I'd expect the plain mov to win by a tiny bit.我不认为应该有太大的速度差异,但如果有的话,我希望普通的mov会赢一点点。

One option is to untangle the sign-extension and the upper value when it is read back, but that can be messy.一种选择是在回读时解开符号扩展和上限值,但这可能会很混乱。

Another option is to construct a union with a bit-packed word.另一种选择是用位压缩字构造联合。 This then defers the problem to the compiler to optimise:然后将问题交给编译器进行优化:

union {
  int64_t merged;
  struct {
     int64_t field1:32,
             field2:32;
  };
};

A third option is to deal with the sign bit yourself.第三种选择是自己处理符号位。 Store a 15-bit absolute value and a 1-bit sign.存储 15 位绝对值和 1 位符号。 Not super-efficient, but more likely to be legal if you should ever encounter a non-2's-complement processor where negative signed values can't be safely cast to unsigned.不是超级高效,但如果您遇到无法将负有符号值安全地转换为无符号的非 2 补码处理器,则更有可能是合法的。 They are rare as hens teeth, so I wouldn't worry about this myself.它们像母鸡的牙齿一样罕见,所以我自己不会担心。

Assuming that the only operation on the 64 bit value will be to convert it back to 32 (and potentially, storing/displaying it), there is no need to apply a mask.假设对 64 位值的唯一操作是将其转换回 32(并可能存储/显示它),则无需应用掩码。 The compiler will sign extend the 32 bit attributes when casting it to 64 bit, and will pick the lowest 32 bit when casting the 64 bit value back to 32 bit.编译器将在将 32 位属性转换为 64 位时对其进行符号扩展,并在将 64 位值转换回 32 位时选择最低的 32 位。

#define packInt32(X) ((uint64_t)(X))
#define unpackInt32(X) ((int)(X))

Or better, using (inline) functions:或者更好的是,使用(内联)函数:

inline uint64_t packInt32(int x) { return ((uint64_t) x) ; }
inline int unpackInt32(uint64_t x) { return ((int) x) ; }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM