我主持原始JPEG标准(ITU 81),特别是图图F.12:扩展V中解码值的符号位:
供参考,SLL
条款的含义为:shift left logical operation
(参见PDF第15页)
现在着名的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标准是否仅限于二进制补码表示?
答案 0 :(得分:3)
JPEG标准是否仅限于两个补码表示?
使用SLL
而非*2
的图表,流程图依赖于2的补码实施。
// ((-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
左操作数量上的行为相同,因为对整数的净影响是数字加倍,并且过程相同unsigned
和signed
数量,只需在数字的右侧部分插入unsigned
,左移所有数字位,表示为右操作符。
0
运算符在>>
和signed
左操作数上的行为,因为净效应再次像整数除以2一样对于右操作数,这意味着,表示<2>的补码表示 扩展最有意义的位(左边的unsigned
变为{{1左边的0
和00
成为1
)以实现除法的净效果,增加到右操作数的幂(11
变为-24 >> 3
,除以2 ^ 3,-3
变为24 >> 3
)使用3
整数时,此行为会发生变化(unsigned
变为0
,00
变为1
)再次将这种变化分成两个部分,提升到正确的操作员的力量。
正如标准所说,行为未定义如果您尝试左/右移位负位数(右运算符必须为01
)或更大或等于左运算符的位大小。但这只影响右操作数,而不影响左操作数。
在尝试获得有关JPEG解码的解释时,只是试着认为JPEG解码使用COSIN转换(对真实向量定义)对作为有符号数量编码的离散实数近似值,或者它使用有符号整数来评估低精确样本,因此它们从不处理无符号数量(库设计者假设对整数进行操作比对浮点数进行操作更快)。
> 0
是二进制-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
({{1圆形到负无穷大)。(请注意,使用此方法进行的负转换会截断到负无穷大,使0
成为0.5
,因为-1 >> 1
会截断为-1
而不会截至-0.5
1}}。)