移位负的有符号值是不确定的

我主演原始的JPEG标准( ITU 81 ),特别是图F.12:扩展V中解码值的符号位

作为参考, SLL术语表示: shift left logical operation (参见PDF的第15页)

图F.12:扩展符号位。

现在着名的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相同的效果 (对于asigned a << n / a >> n )这对于负左操作数从不是未定义的行为,并且为C / C ++定义良好且精确。

好吧,尽管已经选择了已接受的答案并且已经产生了关于<<>>运算符的讨论量,我将尝试澄清什么是未定义的和什么不是。

  • <<运算符在有signedunsigned左操作数量上的行为相同,因为对整数的净影响是数字加倍,并且对于有signedunsigned量的过程是相同的,只需在数字的右边部分插入0 ,左移所有数字位数表示为右操作符。

  • >>运算符在有signedunsigned左操作数上的行为不同 ,因为净效果再次像向右操作数的两个整数除法一样,这意味着, 对于二进制补码表示扩展最有意义的位(左边为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
  • 2400000000....00011000并且右移它变为000000...0000011这是3 。 如果我们再次移动它,它变为000...000111.5舍入到负无穷大),并再次得到000...000000.5舍入到负无穷大)。

(请注意,使用此方法进行的负向移位会截断到负无穷大,使-1 >> 1变为-1 ,因为-0.5截断为-1而不是0