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

时间:2016-11-09 14:09:18

标签: c jpeg libjpeg

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

供参考,SLL条款的含义为:shift left logical operation(参见PDF第15页)

Figure F.12: extend sign bit.

现在着名的libjpeg实现决定实现它this way(非常直接的转录):

/*
 * 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标准是否仅限于二进制补码表示?

3 个答案:

答案 0 :(得分:3)

  

JPEG标准是否仅限于两个补码表示?

使用SLL而非*2的图表,流程图依赖于2的补码实施。

C,OTOH,不限于2的补码,也不使用某些&#34; 2补码&#34;办法。没有这个假设,最好编码。使用无符号类型是很好的第一步。

// ((-1)<<15) + 1
((-1u)<<15) + 1

需要查看HUFF_EXTEND()的应用程序以获得更深入的答案。

答案 1 :(得分:2)

移动负值只是C语言中未定义的行为。在汇编程序级别,这种转变非常好。逻辑/算术移位左指令将MSB移入进位位并将其移位。 CPU不会以不确定的方式停止并着火。

话虽这么说,你可以通过将签名号码转换为无符号来躲避U中的UB。然后,您将只具有实现定义的行为。例如,(int)(-1u<<1)实际上并没有调用UB,即使这样的代码看起来很有问题。

安全方法是对无符号数字(uint32_t)执行所有计算,并仅在实际需要时转换为有符号数。切勿对签名类型执行任何形式的按位操作。

我会完全忽略与不使用两个补码的异国情调/虚构系统的兼容性。专注于与现实主流计算机的兼容性。

答案 2 :(得分:0)

很明显,移动负数与乘以/除以2的幂n 具有相同的效果(a << n / a >> n {{1} } {是a)对于负左操作数,这绝不是未定义的行为,并且对C / C ++进行了很好的精确定义。

好吧,尽管已经选择了已接受的答案以及此问题产生的signed<<运算符的混淆程度,但我会尝试澄清什么& #39;未定义,什么不是。

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

  • 0运算符在>>signed左操作数上的行为,因为净效应再次像整数除以2一样对于右操作数,这意味着,表示<2>的补码表示 扩展最有意义的位(左边的unsigned变为{{1左边的000成为1)以实现除法的净效果,增加到右操作数的幂(11变为-24 >> 3,除以2 ^ 3,-3变为24 >> 3)使用3整数时,此行为会发生变化(unsigned变为000变为1)再次将这种变化分成两个部分,提升到正确的操作员的力量。

正如标准所说,行为未定义如果您尝试左/右移位负位数(右运算符必须为01)或更大或等于左运算符的位大小。但这只影响右操作数,而不影响左操作数。

在尝试获得有关JPEG解码的解释时,只是试着认为JPEG解码使用COSIN转换(对真实向量定义)对作为有符号数量编码的离散实数近似值,或者它使用有符号整数来评估低精确样本,因此它们从不处理无符号数量(库设计者假设对整数进行操作比对浮点数进行操作更快)。

  • > 0是二进制-24,右移1111111...1111101000使其成为311111...11111101。如果我们再次移动它,它会变为-3111...1110-2四舍五入到无穷大),再次-1.51111...1111
  • -124,右移为00000000....00011000,即000000...0000011。如果我们再次移动它,它会变为3000...00011舍入到负无穷大),我们再次获得1.5000...0000({{1圆形到负无穷大)。

(请注意,使用此方法进行的负转换会截断到负无穷大,使0成为0.5,因为-1 >> 1会截断为-1而不会截至-0.5 1}}。)