我有以下C代码(来自FFMPEG):
static inline av_const int sign_extend(int val, unsigned bits)
{
unsigned shift = 8 * sizeof(int) - bits;
union { unsigned u; int s; } v = { (unsigned) val << shift };
return v.s >> shift;
}
我试图在JAVA中重现这一点。但我很难理解这一点。无论我如何折腾,我都不会非常接近。
对于value参数:它将无符号字节值作为int。
位参数:4
如果值为255且位为4.它返回-1。我无法在JAVA中重现这一点。抱歉这样模糊的问题。但是你能帮我理解这段代码吗?
总体而言,我试图在JAVA中编码EA ADPCM音频。在FFMPEG中: https://gitorious.org/ffmpeg/ffmpeg/source/c60caa5769b89ab7dc4aa41a21f87d5ee177bd30:libavcodec/adpcm.c#L981
答案 0 :(得分:3)
严格地说,使用此输入数据运行此代码的结果具有未指定的结果,因为C中的已签名位移仅在此方案不符合的情况下正确定义。从C99标准:
E1 >> E2
的结果是E1
右移E2
位位置。如果E1
具有无符号类型或E1
具有签名类型和非负值,则结果的值是E1
/ 2 {{1的商的整数部分}} 。 如果E2
具有签名类型和负值,则结果值是实现定义的。
(强调我的)
但是,让我们假设您的实现定义了带符号的右移以扩展符号,这意味着如果符号位置位则左侧的空格将填充1,否则为零; ffmpeg代码清楚地预计会出现这种情况。发生以下情况:E1
的值为shift
(假设为32位整数)。用二进制表示法:
28
请注意,在将00000000 00000000 00000000 11111111 = val
11110000 00000000 00000000 00000000 = (unsigned) val << shift
解释为有符号整数时,代码将继续执行(假设two's complement表示,因为今天的计算机都使用 1 ),设置了该整数的符号位,因此右边的符号移位用左边的零填充,我们得到
(unsigned) val << shift
...在两个补码表示中,即-1。
在Java中,这个技巧以相同的方式工作 - 除了更好,因为实际上保证了行为。简单地:
11110000 00000000 00000000 00000000 = v.s
11111111 11111111 11111111 11111111 = v.s >> shift
或者,如果您愿意:
public static int sign_extend(int val, int bits) {
int shift = 32 - bits; // int always has 32 bits in Java
int s = val << shift;
return s >> shift;
}
1 严格地说,由于历史原因,这种转换在C标准中也没有明确定义的值。曾经有过使用不同表示的计算机,并且具有设置符号位的相同位模式在(例如)带符号的幅度表示中具有完全不同的含义。
答案 1 :(得分:0)
代码看起来很奇怪的原因是C语言充满了Java中定义明确的未定义行为。例如,在C位移位有符号整数 left ,以便符号位更改为未定义行为,此时程序可以执行任何 > - 无论编译器导致程序执行什么操作 - 崩溃,打印42,make true = false,任何事情都可能发生,编译器仍然可以正确编译它。
现在代码使用1个技巧来移动左边的整数:它使用一个联合,它将成员的字节放在彼此的顶部 - 使无符号和有符号整数占用相同的字节; bitshift用unsigned integer
定义;所以我们使用它进行无符号转换;然后使用带符号的移位返回(代码假定负数的右移产生正确的符号扩展的负数,这也不是标准保证的,但通常这些类型的库有一个配置实用程序,可以拒绝编译一个非常深奥的平台;同样这个程序假定CHAR_BIT
是8;但是C只保证char
至少 8位宽。
在Java中,你不需要像联盟这样的东西来完成这个任务;相反,你做:
static int signExtend(int val, int bits) {
int shift = 32 - bits; // fixed size
int v = val << shift;
return v >> shift;
}
在Java中,int
的宽度始终为32位; <<
可用于有符号和无符号转换;并且没有未定义的行为扩展到符号位; >>
可用于签名班次(>>>
将无签名)。
答案 2 :(得分:0)
given this code:
static inline av_const int sign_extend(int val, unsigned bits)
{
unsigned shift = 8 * sizeof(int) - bits;
union { unsigned u; int s; } v = { (unsigned) val << shift };
return v.s >> shift;
}
'static'修饰符表示该函数在当前文件外部不可见。
'inline'修饰符是对编译器的“请求”,即在调用函数时将代码“内联”放置,而不是具有与关联的调用/返回代码序列分开的函数
'sign_extend'是函数的名称
in C, a right shift, for a signed value will propagate the sign bit,
In C, a right shift, for a unsigned value will zero fill.
It looks like your java is doing the zero fill.
regarding this line:
unsigned shift = 8 * sizeof(int) - bits;
on a 32bit machine, an integer is 32 bits and size of int is 4
so the variable 'shift' will contain (8*4)-bits
regarding this line:
union { unsigned u; int s; } v = { (unsigned) val << shift };
left shift of unsigned will shift the bits left,
with the upper bits being dropped into the bit bucket
and the lower bits being zero filled.
regarding this line:
return v.s >> shift;
this shifts the bits back to their original position,
while propagating the (new) sign bit