理解java中的带符号数和补码

时间:2013-02-04 19:53:18

标签: java byte bit sign complement

我有一个3字节的有符号数,我需要确定Java中的值。我相信它是用一个补码签名但我不是100%肯定(我已经十多年没研究过这个东西了,我的问题的记录并不是很清楚)。我认为我遇到的问题是Java所做的一切都是两个补码。我有一个具体的例子来说明:

  • 原始的3字节数字:0xEE1B17

  • 解析为整数(Integer.parseInt(s, 16))后,这将成为:15604503

  • 如果我做了一个简单的翻转(~),我得到(我认为)一个二进制补码表示:-15604504

  • 但我应该得到的价值是:-1172713

我认为正在发生的是我得到整个int的两个补码而不仅仅是int的3个字节,但我不知道如何解决这个问题。

我能做的是将整数转换为二进制字符串(Integer.toBinaryString()),然后手动将所有0转换为1,反之亦然。然后在解析这个整数(Integer.parseInt(s, 16))时,得到非常接近的1172712。在所有其他示例中,我需要始终将1添加到结果中以获得答案。

任何人都可以诊断这里使用的是哪种类型的带符号数字编码,是否有除手动翻转字符串的每个字符之外的解决方案?我觉得必须有一个更优雅的方式来做到这一点。

编辑:所有的响应者都以不同的方式提供帮助,但我的一般问题是如何翻转一个3字节的数字,@ louis-wasserman回答了这个并首先回答,所以我将他标记为解决方案。感谢大家的帮助!

3 个答案:

答案 0 :(得分:2)

如果要翻转Java int的低三个字节,那么只需执行^ 0x00FFFFFF

答案 1 :(得分:1)

0xFFEE1B17是 - 1172713 您只能添加前导字节。如果设置了3字节值的最高位,则FF,否则为00

将3字节值转换为适当的int的方法可能如下所示:

if(byte3val>7FFFFF)
  return byte3val| 0xFF000000;
else 
  return byte3val;

答案 2 :(得分:0)

定义负的有符号数字,以便a + (-a) = 0。因此,这意味着所有位都被翻转,然后添加1。见Two's complement。您可以通过考虑添加a + ~a + 1时发生的情况来检查此过程是否满足条件。

您可以通过最重要的位识别出一个数字为负数。因此,如果需要将带符号的3字节数转换为4字节数,可以通过检查该位来实现,如果设置了,则还要设置第4个字节的位:

if ((a & 0x800000) != 0)
    a = a | 0xff000000;

您也可以在单个表达式中执行此操作,该表达式很可能表现更好,因为计算中没有分支(分支在当前CPU中与pipelining不兼容):

a = (0xfffffe << a) >> a;

此处<<>>执行字节转换。首先,我们将数字8位向右移动(所以现在它占据3“上”字节而不是3“下”字节),然后将其移回。诀窍是>>是所谓的Arithmetic shift,也称为签名转移。将最高有效位复制到操作空闲的所有位。这正是为了保持数字的符号。事实上:

(0x1ffffe << 8) >> 8        ->  2097150
(0xfffffe << 8) >> 8        ->  -2

请注意,java也有一个无符号右移运算符>>>。有关更多信息,请参阅Java教程:Bitwise and Bit Shift Operators