【问题标题】:Suppressing sign-extension when upcasting or shifting in Java在 Java 中向上转换或移位时抑制符号扩展
【发布时间】:2011-02-28 21:24:36
【问题描述】:

我觉得这是一个相当微不足道的问题,但我很难过。在我的应用程序中,我用一对整数在查找表中键入内容。我认为将两个 int 连接成一个 long 并使用单个 long 作为键会更容易。来自 C 背景,我希望这样的事情能奏效:

int a, b;
long l = (long)a << 32 | b;

我在 Java 中复制这一点的尝试让我感到沮丧。特别是,因为没有无符号整数类型,我似乎无法避免 b 的自动符号扩展(a 左移,所以它无关紧要)。我试过使用b &amp; 0x00000000FFFFFFFF,但令人惊讶的是没有效果。我也尝试了相当丑陋的(long)b &lt;&lt; 32 &gt;&gt; 32,但它似乎被编译器优化了。

我希望严格使用带有原语的位操作来做到这一点,但我开始怀疑是否需要使用某种缓冲区对象来实现这一点。

【问题讨论】:

  • (long)b &lt;&lt; 32 &gt;&gt; 32 未优化。主要问题是 >> 是 sra 而 >>> 是 srl,这就是你想要的:(long)b &lt;&lt; 32 &gt;&gt;&gt; 32
  • 表达式b &amp; 0x00000000FFFFFFFF 必须有效,但为什么要用这些零来混淆它?
  • @maaartinus:我对 Java 不够熟悉,不知道它在内部将十六进制文字存储为什么。我知道在 C 十六进制文字中是无符号的,但我担心 0xFFFFFFFF 会被符号扩展。事后看来,我只需要一个前导零就可以满足这种担忧。表达式仍然没有效果。
  • 当然,0xFFFFFFFF 不会进行符号扩展。但是还有另一个问题,它只是一个int(我们都忘记了L 后缀)并被提升为long - 当然是通过符号扩展。这就是为什么它不能工作。前导零没有任何变化,文字只是一个 int。看我的回答,肯定是对的,我用过很多次了。
  • 你所做的就像C中的(signed int) (b &amp; 0xFFFFFFFF)。结果是一个有符号的int,并通过符号扩展提升为long。

标签: java


【解决方案1】:

我总是使用我的实用程序类

public static long compose(int hi, int lo) {
    return (((long) hi << 32) + unsigned(lo));
}
public static long unsigned(int x) {
    return x & 0xFFFFFFFFL;
}

public static int high(long x) {
    return (int) (x>>32);
}
public static int low(long x) {
    return (int) x;
}

对于任何int x, y(否定与否)

high(compose(x, y)) == x
low(compose(x, y)) == y

持有并适用于任何long z

compose(high(z), low(z)) == z

也成立。

【讨论】:

  • 如果可以的话,我会不止一次地给你投票,标志扩展让我发疯了。
  • 不应该high 使用&gt;&gt;&gt; 而不是&gt;&gt;
  • @JamesKo 移位后的值仅在其高 32 位上有所不同,即在那些被演员删除的地方。您可能想使用无符号移位来证明您没有处理签名,但我选择使用有符号移位来证明它并不重要。 ;)
  • 从 Java 1.8 开始,现在有 Integer.toUnsignedLong 来进行这种转换
【解决方案2】:

我不时这样做 - 我将两个整数存储在我的 X Y 坐标中。因为我知道我的范围永远不会超过 10 亿,所以我执行以下操作:

private Long keyFor(int x, int y) {
    int kx = x + 1000000000;
    int ky = y + 1000000000;
    return (long)kx | (long)ky << 32;
}

private Long keyFor(int[] c) {
    return keyFor(c[0],c[1]);
}

private int[] coordsFor(long k) {
    int x = (int)(k & 0xFFFFFFFF) - 1000000000;
    int y = (int)((k >>> 32) & 0xFFFFFFFF) - 1000000000;
    return new int[] { x,y };
}

【讨论】:

  • 这不是通用的解决方案,并且使组合难以破译。以正常方式组合数字可以工作,速度更快,并且可以直接在十六进制输出中查看组件。
  • “以正常方式组合数字”不起作用——如果它起作用了,我就不会走这条路。这是 OP 正在解决的确切问题。这并不难理解。我当然不认为这是投反对票的标准,因为这是一个潜在的解决方案,即使你觉得它比必要的更加模糊。
  • 我不喜欢它,因为它引入了不必要的混淆。假设您像我一样阻止符号扩展,那么以“正常方式”进行操作。那么你不需要添加常数,也不需要对所涉及的数字进行任何事情
  • 通过不提供常数,您将自己限制为仅正数。所以你实际上是在对你的数字做出假设,不管你喜不喜欢。
  • 我不限制自己为正数!查看我编辑的答案,尝试找到一个反例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-05-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-03
  • 1970-01-01
  • 2016-06-10
相关资源
最近更新 更多