移位负的有符号值是不确定的
我主演原始的JPEG标准( ITU 81 ),特别是图F.12:扩展V中解码值的符号位 :
作为参考, SLL
术语表示: shift left logical operation
(参见PDF的第15页)
现在着名的libjpeg实现决定以这种方式实现它(非常直接的转录):
/* * Figure F.12: extend sign bit. * On some machines, a shift and add will be faster than a table lookup. */ #ifdef AVOID_TABLES #define HUFF_EXTEND(x,s) ((x) < (1<<((s)-1)) ? (x) + (((-1)<<(s)) + 1) : (x)) #else #define HUFF_EXTEND(x,s) ((x) < extend_test[s] ? (x) + extend_offset[s] : (x)) static const int extend_test[16] = /* entry n is 2**(n-1) */ { 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 }; static const int extend_offset[16] = /* entry n is (-1 << n) + 1 */ { 0, ((-1)<<1) + 1, ((-1)<<2) + 1, ((-1)<<3) + 1, ((-1)<<4) + 1, ((-1)<<5) + 1, ((-1)<<6) + 1, ((-1)<<7) + 1, ((-1)<<8) + 1, ((-1)<<9) + 1, ((-1)<<10) + 1, ((-1)<<11) + 1, ((-1)<<12) + 1, ((-1)<<13) + 1, ((-1)<<14) + 1, ((-1)<<15) + 1 }; #endif /* AVOID_TABLES */
很明显,转移负面符号值是UB,所以我想知道JPEG标准的原作者在这里实际意味着什么。 JPEG标准是否仅限于二进制补码表示?
JPEG标准是否仅限于二进制补码表示?
使用SLL
而不是*2
的图表,流程图依赖于2的补码实现。
C,OTOH,不限于2的补码,也不使用某种“2的补码”方式的移位。 没有这个假设,最好编码。 使用无符号类型是很好的第一步。
// ((-1)<<15) + 1 ((-1u)<<15) + 1
需要查看HUFF_EXTEND()
应用程序以获得更深入的答案。
转移负值只是C语言中未定义的行为。 在汇编程序级别上,这种转换非常精细。 逻辑/算术移位左指令将MSB移入进位位,就是这样。 CPU不会以不确定的方式停止并着火。
话虽这么说,你可以通过将签名号码转换为无符号来躲避UB。 然后,您将只具有实现定义的行为。 例如, (int)(-1u<<1)
实际上并没有调用UB,即使这样的代码看起来很有问题。
安全的方法是对无符号数( uint32_t
)执行所有计算,并仅在实际需要时转换为signed。 切勿对签名类型执行任何forms的按位操作。
我会完全忽略与不使用二进制补码的异国情调/虚构系统的兼容性。 专注于与现实主流计算机的兼容性。
很明显,移位负数具有与乘以/除以幂2相同的效果 (对于a
有signed
a << n
/ a >> n
)这对于负左操作数从不是未定义的行为,并且为C / C ++定义良好且精确。
好吧,尽管已经选择了已接受的答案并且已经产生了关于<<
和>>
运算符的讨论量,我将尝试澄清什么是未定义的和什么不是。
-
<<
运算符在有signed
和unsigned
左操作数量上的行为相同,因为对整数的净影响是数字加倍,并且对于有signed
和unsigned
量的过程是相同的,只需在数字的右边部分插入0
,左移所有数字位数表示为右操作符。 -
>>
运算符在有signed
和unsigned
左操作数上的行为不同 ,因为净效果再次像向右操作数的两个整数除法一样,这意味着, 对于二进制补码表示 , 扩展最有意义的位(左边为0
变为00
和左边的1
成为11
)实现除法的净效果提升到右操作数的幂(-24 >> 3
变为-3
,除以2 ^ 3和24 >> 3
变为3
)当使用unsigned
整数时,此行为会发生变化(0
变为00
变为01
),再次将变换分成两个加权到右运算符的幂。
正如标准所说, 如果你试图向左/右移位负数位 (右运算符必须> 0
)或者大于或等于左运算符的大小(以位为单位 ),行为是不确定的。 但这只影响右操作数,而不影响左操作数。
在尝试获得有关JPEG解码的解释时,只是试着认为JPEG解码使用COSIN转换(对真实矢量定义)对作为有符号量编码的离散实际近似值,或者,它使用有符号整数来评估低精度样本,所以他们从不处理无符号数量(图书馆设计者认为对整数进行操作比对浮点数进行操作要快)。
注意
-
-24
是二进制1111111...1111101000
,右移3
使其成为11111...11111101
,或-3
。 如果我们再次移动它,它变为111...1110
或-2
(-1.5
舍入到负无穷大),再次1111...1111
或-1
。 -
24
是00000000....00011000
并且右移它变为000000...0000011
这是3
。 如果我们再次移动它,它变为000...0001
或1
(1.5
舍入到负无穷大),并再次得到000...0000
或0
(0.5
舍入到负无穷大)。
(请注意,使用此方法进行的负向移位会截断到负无穷大,使-1 >> 1
变为-1
,因为-0.5
截断为-1
而不是0
)