C++中的24位到32位转换
24-bit to 32-bit conversion in C++
我需要在C++中将一个24位整数(2s互补)转换为32位整数。我在这里找到了一个解决方案,它被称为
int interpret24bitAsInt32(unsigned char* byteArray)
{
return (
(byteArray[0] << 24)
| (byteArray[1] << 16)
| (byteArray[2] << 8)
) >> 8;
}
尽管我发现它正在工作,但我对这段代码有以下担忧。byteArray[0]
只有8位,因此像byteArray[0] << 24
这样的操作如何可能?如果编译器将byteArray上转换为整数并执行此操作,则这是可能的。这可能就是它现在起作用的原因。但我的问题是,这种行为是否在所有编译器中都得到了保证,并在标准中明确提及?这对我来说并不是微不足道的,因为我们没有明确地向编译器提供目标是32位整数的任何线索!
此外,请让我知道,任何像矢量化这样的即兴操作都有可能提高速度(可能使用C++11),因为我需要将大量的24位数据转换为32位。
int32_t interpret24bitAsInt32(unsigned char* byteArray)
{
int32_t number =
(((int32_t)byteArray[0]) << 16)
| (((int32_t)byteArray[1]) << 8)
| byteArray[2];
if (number >= ((int32_t)1) << 23)
//return (uint32_t)number | 0xFF000000u;
return number - 16777216;
return number;
}
这个函数应该在不调用未定义行为的情况下,通过将1
移位到int
的符号位来执行您想要的操作
只有在sizeof(int) < 4
的情况下才需要int32_t
强制转换,否则将默认整数升级为int
。
如果有人不喜欢if
:编译器没有将其转换为条件跳转(gcc 9.2):https://godbolt.org/z/JDnJM2
它留下一个cmovg
。
[expr.shift]/1操作数应为整数或无范围枚举类型,并执行整数提升。结果的类型是提升的左操作数的类型。。。
[conf.prom]7.6整体促销
1如果int可以表示源类型的所有值,则整数转换秩(7.15)小于
int
的秩的除bool
、char16_t
、char32_t
或wchar_t
之外的整数类型的prvalue可以转换为int
类型的prvalue;否则,可以将源prvalue转换为类型为unsigned int
的prvalue。
因此,是的,标准要求在求值之前将类型为unsigned char
的移位运算符的自变量提升为int
。
也就是说,代码中的技术依赖于int
a)是32位大,b)使用两个补码来表示负值。这两者都没有得到标准的保证,尽管这在现代系统中很常见。
没有分支的版本;但是乘法:
int32_t interpret24bitAsInt32(unsigned char* bytes) {
unsigned char msb = UINT8_C(0xFF) * (bytes[0] >> UINT8_C(7));
uint32_t number =
(msb << UINT32_C(24))
| (bytes[0] << UINT32_C(16)))
| (bytes[1] << UINT32_C(8)))
| bytes[2];
return number;
}
不过,您需要测试省略分支是否真的能给您带来性能优势!
改编自我的旧代码,该代码针对10位数字执行此操作。使用前测试!
哦,它仍然依赖于关于uint32_t
到int32_t
的转换的实现定义的行为。如果你想去兔子洞,那就玩得开心,但要小心。
或者,更简单的是:使用mchs答案中的技巧。也使用移位而不是乘法:
int32_t interpret24bitAsInt32(unsigned char* bytes) {
int32_t const number =
(bytes[0] << INT32_C(16))
| (bytes[1] << INT32_C(8))
| bytes[2];
int32_t const correction =
(bytes[0] >> UINT8_C(7)) << INT32_C(24);
return number - correction;
}
测试用例
对于运算符算术,确实存在小于int
的类型的Integral_production
所以假设sizeof(char) < sizeof(int)
在中
byteArray[0] << 24
byteArray
在int
中被提升,并且您在int
上进行位移位。
第一个问题是int
只能是16位。
第二个问题(在C++20之前),int
是有符号的,逐位移位可以很容易地导致实现定义或UB(对于负24位数字,两者都有)。
在C++20中,逐位移位的行为被简化了(定义了行为),有问题的UB也被删除了。
负数的前导CCD_ 29保留在CCD_ 30中。
因此,在C++20之前,您必须执行以下操作:
std::int32_t interpret24bitAsInt32(const unsigned char* byteArray)
{
const std::int32_t res =
(std::int32_t(byteArray[0]) << 16)
| (byteArray[1] << 8)
| byteArray[2];
const std::int32_t int24Max = (std::int32_t(1) << 24) - 1;
return res <= int24Max ?
res : // Positive 24 bit numbers
int24Max - res; // Negative number
}
对移位表达式[expr.shift]/1的操作数执行积分提升[conv.promm]。在您的情况下,这意味着在应用<<
[conv.prom]/1之前,unsigned char
类型的值将转换为int
类型。因此,C++标准保证操作数是"上转换"的。
然而,该标准仅保证int
具有至少16位。也不能保证unsigned char
具有恰好8位(它可能具有更多)。因此,不能保证int
总是足够大以表示这些左移的结果。如果int
恰好不够大,则生成的有符号整数溢出将调用未定义的行为[expr]/4。很可能int
在您的目标平台上有32位,因此,一切最终都会解决。
如果你需要使用有保证的、固定数量的比特,我通常建议使用固定宽度的整数类型,例如:
std::int32_t interpret24bitAsInt32(const std::uint8_t* byteArray)
{
return
static_cast<std::int32_t>(
(std::uint32_t(byteArray[0]) << 24) |
(std::uint32_t(byteArray[1]) << 16) |
(std::uint32_t(byteArray[2]) << 8)
) >> 8;
}
请注意,负值的右移目前是由实现定义的[expr.shift]/3。因此,不能严格保证此代码最终会对负数执行符号扩展。然而,您的编译器需要记录负整数的右移操作[dfns.impl.defined](即,您可以确保它能满足您的需要)。我从来没有听说过编译器在实践中不将负值的右移作为算术移位。此外,看起来C++20将强制执行算术移位行为…
- C++将 16 位值转换为 32 位值
- C++中的24位到32位转换
- 我把我的编译器从32位转换为64位,但我仍然不能使用超过2GB:(为什么
- 十进制,包括负到 32 位二进制转换器
- 将 VS 32 位项目转换为 64
- 将 32 位大端有符号整数转换为有符号小端整数
- 如何将 32 位编译二进制转换为 64 位
- 无符号 int 与无符号长 两者都是 32 位,但我无法在不转换的情况下混合它们......为什么?
- C++:如何将 32 位数据转换为有符号整数
- 将 32 位浮点数和不强制转换的 32 位整数与双精度进行比较,当其中一个值可能太大而无法完全适合另一种类型时
- 如何在AVX2中从32位转换为16位未签名的整数
- 使用 std::chrono 将 32 位 unix 时间戳转换为 std::string
- 如何在Visual C 中的BSTR和32位Unicode字符串之间进行转换
- 在 C/C+ 中从 16 位线性 PCM 音频转换为 32 位浮点数的最佳方法
- 将大端序的 32 位变量转换为小端序
- 如何在C++中将十六进制转换为IEEE 754 32位浮点
- 将SSE转换为霓虹灯:如何打包然后提取32位结果
- 将二进制值转换为十六进制值(32位)
- 是否有任何中间件/库可以将二进制或文本数据从64位转换为32位
- 在MS VC 2013 Express中将C++dll从32位转换为64位