【问题标题】:How to perform unsigned to signed conversion in Java?如何在 Java 中执行无符号到有符号的转换?
【发布时间】:2011-04-25 12:27:38
【问题描述】:

假设我从输入设备读取了这些字节:“6F D4 06 40”。该数字是 MilliArcSeconds 格式的经度读数。最高位(0x80000000)基本上总是零,并且在这个问题中被忽略。

我可以轻松地将字节转换为 无符号 整数:1876166208

但是如何将无符号值转换为其最终形式的 31 位有符号整数?

到目前为止,我想出的只是:

  1. 如果 value & 0x40000000 那么它实际上是负数,需要转换它
  2. 如果是负数,则去掉最高位并处理剩余位...

所以我可以判断它是否是一个负数,但为了知道负数是什么值,我必须对剩余的位做一些事情 - 一个人的恭维?我如何在 Java 中做到这一点?

另一种提问方式是,如何在 Java 中将无符号整数转换为 31 位有符号整数?

谢谢!

【问题讨论】:

  • 我想我在回答中误解了你的问题。根据您的示例输入,您能说出您希望有符号整数的值是多少吗?你从来没有说过。
  • 输入:“6F D3 FD 45”。输出:大约 -75.366465。我说近似值是因为我找到了一个地点并留在那里收集数据,同时用 GPS 测量了位置。因此,上面的经度 (-75.366465) 应该接近,但可能不完全相同,与上述十六进制字节转换为有符号整数(并执行 MAS 到度数转换)的值相同。 我正在尝试找到一种使用 Java 将十六进制字节转换为有符号整数的方法
  • MAS 度数:除以 3,600,000。
  • 接近... 去除“负位”的实际读数:“2F D3 FD 45”...将此与“10 2C 00 EA”的预期 MAS 值 -75.366465 进行比较。 . 如果我将 1 翻转为 0 并将 0 翻转为 1 (XOR),则前 22 位匹配。我认为这可能是诀窍 - 需要翻转所有位......我可以用 XOR 来做到这一点......
  • @Mark - 你是对的。我很抱歉。

标签: java type-conversion signed


【解决方案1】:

答案取决于输入的低 31 位要表示的内容。

int input = 0x6FD40640 & 0x7FFFFFFF; //strip top bit; only here for clarity

无符号输入:0x6FD40640 == 1876166208

二进制补码(期望输出:-271317440)

二的补码整数是一个整数,其中 -1 设置了所有位,并且较低的负数从那里向下计数。第一位仍然充当符号位。

1000 -> -8
1001 -> -7
...
1110 -> -2
1111 -> -1
0000 ->  0
0001 ->  1

如果低 31 位表示二进制补码整数,那么我认为您应该能够做到这一点:

input = (input << 1) >> 1;

这是因为 Java 在内部将整数存储在二进制补码中:我们所做的只是左移然后右移(有符号),以便拾取符号位并且整数从 31 位变为 32 位。

一个补码(期望输出:-802424384)

一个补码数字表示是第一位是专用符号位,其余位表示幅度。 -100 的低位将与 100 的低位相同:

 1111 -> -7
 1110 -> -6
 ...
 1001 -> -1
 1000 -> -0 (anomoly)
 0000 ->  0
 0001 ->  1

如果低 31 位表示一个一个补码整数(即一个符号位后跟 30 位表示一个无符号量值),那么需要将其转换为二进制补码,以便 Java 提取正确的值。为此,您只需提取低 30 位并乘以 -1:

if ( input & 0x40000000 ) {
   input = (input & 0x3FFFFFFF) * -1;
}

您在问题的 cmets 中说,在转换为度数(除以 3600000)后,您会得到 -75.36 左右。 当我将 -271317440 除以 3600000 时,我得到 -75.36595555555556,所以我猜你的输入格式是二进制补码,所以我的第一个原始答案是正确的。

【讨论】:

  • 看起来可以解决问题。不过不确定&amp; 0x7FFFFFFF 是否必要。
  • 似乎 &7fffffff 和左/右移位做同样的事情(删除最左边的最高位)?我正在努力获取一些样本数据,以便我们对此进行测试
  • @Alison:不,那只是为了证明输入只是低 31 位,而高位无关紧要。第二行是我试图展示的内容,但 Brad 尚未确认这是否是所需的输出。
  • @Brad: 只需与&amp;7FFFFFFF 匹配就可以清除最高位。这向左移动,但以符号扩展的方式向右移动。换句话说,它用第二位填充第一位。但是我再次需要看看你想要的输出是什么......我不知道低 31 位应该代表什么。
  • 为什么这个答案得到了投票?它根本没有解决这个问题。请重新阅读问题!
【解决方案2】:

将无符号整数读取为有符号是确定是否设置了最高有效位(负标志),如果是,则翻转数字的所有位(从而清除最高有效位,并将数字切换到它的负表示。在执行上述过程时,您还必须注意结果数字是负数这一事实。

// Convert the hex bytes to an unsigned integer
long MAS = Integer.ValueOf("6F D4 06 40".replace (" ",""),16);
boolean isLongitudeNegative = false;

// Is the negative-bit set? If so, strip it and toggle all bits.
if (MAS & 0x40000000 > 0) {
    // then it's negative, so strip the negative bit and flip all the other bits
    MAS ^= 0xFFFFFFFF;
    // Throw away the unused bit.
    MAS &= 0x7FFFFFFF;
    isLongitudeNegative = true;
}

// Now convert from MAS to degrees minutes seconds
String DMS = convertMASToDMS(isLongitudeNegative,MAS);

【讨论】:

  • 对爆发感到抱歉。这个答案是否最终满足了您的要求?如果是这样,请随意接受。唯一的建议是您可能会发现MAS = ~MAS 是一种更清晰的方式来切换 MAS 的位。所以给定这个解决方案,我假设输入的低 31 位代表一个 31 位的补码整数,并且您正在尝试提取该值?这就是您要解决的问题吗?
  • 再次检查我的答案。我很确定我原来的答案是正确的。当我将转换应用于您的样本输入然后除以 3600000 时,我得到 -75.369555,这是您的预期输出。当我进行你的转换时,我得到 -222,它不会跳动。
猜你喜欢
  • 2011-10-25
  • 1970-01-01
  • 2012-11-18
  • 2011-05-30
  • 1970-01-01
  • 2023-03-16
  • 1970-01-01
  • 2014-10-29
  • 2013-06-23
相关资源
最近更新 更多