【问题标题】:Sign extension, bit shifting in JAVA. Help understanding a C-code bit符号扩展,JAVA 中的位移。帮助理解 C 代码位
【发布时间】:2015-05-29 17:19:48
【问题描述】:

我有以下 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

【问题讨论】:

  • 不太熟悉Java,但是Java有two different right-shift operators:有符号(&gt;&gt;)和无符号(&gt;&gt;&gt;)。在 C 中,区别在于移位者的类型。在您的 Java 代码中,您需要 &gt;&gt;&gt;
  • 我尝试了 >>>,但显然我不明白代码到底在做什么。 (255 >>> 4) 在 JAVA 中的结果是 15,而不是 C 函数返回的 -1。
  • C 代码不适用于 BYTES,它正在使用 INTEGERS。在现代硬件中,整数要么是 32 位(或者最近是 64 位)
  • 'av_const' 修饰符有什么作用?

标签: java c bit


【解决方案1】:

严格来说,使用此输入数据运行此代码的结果具有未指定的结果,因为 C 中的有符号位移位仅在此场景不满足的情况下才正确定义。来自 C99 标准:

E1 &gt;&gt; E2 的结果是E1 右移E2 位位置。如果E1 具有无符号类型或E1 具有有符号类型和非负值,则结果的值是E1 / 2E2 商的整数部分。 如果E1 具有带符号类型和负值,则结果值是实现定义的。

(强调我的)

但是让我们假设您的实现定义了有符号右移来扩展符号,这意味着如果设置了符号位,则左侧的空间将填充为 1,否则为零; ffmpeg 代码显然希望是这种情况。正在发生以下情况:shift 的值为 28(假设为 32 位整数)。二进制:

00000000 00000000 00000000 11111111 = val
11110000 00000000 00000000 00000000 = (unsigned) val << shift

请注意,当将(unsigned) val &lt;&lt; shift 解释为有符号整数时,随着代码继续执行(假设two's complement 表示,因为今天的计算机都使用1),该整数的符号位被设置, 所以一个有符号的右移会从左边填满零,我们得到

11110000 00000000 00000000 00000000 = v.s
11111111 11111111 11111111 11111111 = v.s >> shift

...在二进制补码表示中,即 -1。

在 Java 中,这个技巧的工作方式相同——除了更好,因为那里的行为实际上是有保证的。简单地说:

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;
}

或者,如果您愿意:

public static int sign_extend(int val, int bits) {
  int shift = 32 - bits;
  return val << shift >> shift;
}

1 严格来说,由于历史原因,这种转换在 C 标准中也没有明确定义的值。曾经有使用不同表示的计算机,并且具有设置符号位的相同位模式在(例如)有符号幅度表示中具有完全不同的含义。

【讨论】:

  • 您实际上可以只使用shift = - bits,因为 Java 中的所有整数移位都被屏蔽到它们的低 5 位。
【解决方案2】:

代码看起来如此奇怪的原因是 C 语言充满了未定义的行为,而在 Java 中这些行为是明确定义的。例如,在 C 中,对有符号整数 left 进行位移,以便符号位的变化是 未定义的行为,此时程序可以执行 任何操作 - 无论编译器导致程序做什么 - 崩溃,打印 42,使 true = false,任何事情都可能发生,并且编译器仍然正确编译它。

现在代码使用 1 技巧将整数左移:它使用联合将成员的字节放在彼此的顶部 - 使无符号整数和有符号整数占据相同的字节;位移是用unsigned integer 定义的;所以我们使用它进行无符号移位;然后使用带符号的移位返回(代码假设负数的右移会产生正确的符号扩展负数,这也不是标准的保证,但通常这些类型的库有一个配置实用程序可以拒绝在此类上编译一个相当深奥的平台;同样,这个程序假定CHAR_BIT 是8;但是C 只保证char至少 8 位宽。

在 Java 中,你不需要像 union 这样的东西来完成这个;相反,你这样做:

static int signExtend(int val, int bits) {
    int shift = 32 - bits;  // fixed size
    int v = val << shift;
    return v >> shift;
}

在 Java 中,int 的宽度始终为 32 位; &lt;&lt; 可用于有符号和无符号移位;并且没有未定义的行为可以扩展到符号位; &gt;&gt; 可用于有符号移位(&gt;&gt;&gt; 将是无符号的)。

【讨论】:

    【解决方案3】:
    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;
    }
    

    “静态”修饰符表示该函数在当前文件之外不可见。

    “内联”修饰符是对编译器的“请求”,将代码“内联”放置在调用函数的任何位置,而不是使用具有相关调用/返回代码序列的单独函数

    '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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-08-18
      • 1970-01-01
      • 2010-11-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多