【问题标题】:Ideas to deal with "unsigned" integers in Java 8在 Java 8 中处理“无符号”整数的想法
【发布时间】:2015-03-17 21:41:51
【问题描述】:

我正在编写与硬件和数学相关的代码,并广泛使用 32 位和 64 位无符号整数值。如果没有任何 javac 支持,错误一直很稳定且难以解决。 Java 8 在盒装的 Long 类上添加了一些用于除法、模数和比较的函数,但是虽然这些函数提供了运行时支持,但由于无意混合两者而缺乏编译时警告,这让我想把头发拉出来.

有没有人知道有什么方法可以帮助解决这些问题? Java 团队的一位成员提到了possible annotation support 进行类型检查。

到目前为止,我在所有无符号变量前加上 u_,我什至尝试在每次出现应该无符号的 intlong 之前添加 /* unsigned */ 注释。虽然这些很有用,但它们也非常混乱。它还不够远。犯错的机会太多了。

两个较大的问题是不需要的符号扩展和混合操作。

/* unsigned */ long u_lowmask = 0xffffffff 这样无害的东西并没有得到想要的结果,甚至只是/* unsigned */ long u_widen64 = small32 用那个无声的扩展名破坏了事情。在其他语言中,我可以从编译器或类似 Lint 的静态检查器中获得警告。函数式语言通常将其构建到类型检查机制中,但 Java 避开了这些,转而支持其他解决方案。混合比较或操作的机会太多了。

任何想法都应该不会对运行时产生影响,所以我不能将 unsigned 包装在一个类或 bignums 中。好吧,如果我可以在没有任何分配或线程局部变量的费用的情况下侥幸完成它,我也许可以将它们包装在一个类中,但这可能会使代码不可重入。此外,Hotspot 还必须能够为接近于简单本机的包装无符号整数发出代码。 (实际上,我认为 Hotspot 可能足够聪明,可以接近 - 可能需要额外的内存读写。)

其他人如何围绕这个问题进行编码?

【问题讨论】:

  • 重写以删除对库或工具的任何引用。
  • 我认为,基于注释的解决方案将是最干净的,但目前还没有合适的检查工具。

标签: java annotations java-8 unsigned


【解决方案1】:

编辑:截至 2016 年 7 月,Checker Framework 提供了您所要求的大部分或全部内容:Signedness Checker,它验证了有符号值与无符号值的一致使用。来自manual

签名检查器保证有符号和无符号值不会在计算中混合在一起。此外,它还禁止无意义的操作,例如对无符号值的除法。

(向@MAGx2 致敬,因为他注意到这个答案已经过时了。)


旧答案如下:

我建议您查看Checker Framework。它使您能够定义自己的类型限定符注释,例如@Unsigned,然后在编译时检查您的代码对于这些注释的类型是否正确。如果它没有发出警告,那么您可以保证您的代码不会混合有符号和无符号值。

Checker 框架附带20 type-checkers,但没有用于无符号算术。您需要write your own type-checker。这应该相对简单,因为类型检查器需要很少的特殊规则:您只是不想混合有符号和无符号值。

有关需要库注释的 JDK 方法列表,请参阅 http://hg.openjdk.java.net/jdk8/tl/jdk/rev/71200c517524

【讨论】:

  • 这可能与我将得到的一样接近。我会调查的。这也可能是一个很好的例子,说明为什么这里的一些关于 SO 的规则编写和遵循都很差。我对这个问题进行了四次重写——所有以前的问题都被关闭为询问库或工具的建议——最终得到一个库或工具的答案。一个开发者的语言是另一个开发者的库。
  • 我发现Checker Framework checkerframework.org/manual/#signedness-checker中有签名/未签名的检查
【解决方案2】:

在检测端,我没有研究太多,但我会检查Lint4jthis answer 的其他Java lint 工具。

理想情况下,该工具会在每次将 long 变量分配给 int 值时向您发出警告,然后您可以使用如下解决方案将每个此类分配包装在静态(可内联)方法调用中(从而消除警告)。

对于赋值静态包装器方法,过去像这样的实用程序类对我来说效果很好:

public class Convert {
    private Convert() {} // static utility methods only

    public long uintToUlong(int uint) {
        long ulong = 0;
        if (uint < 0) {
            uint &= Integer.MAX_VALUE;
            ulong = Integer.MAX_VALUE;
            ulong++;
        }
        return ulong + uint;
    }

    public long uintToLong(int uint) {
        return uintToUlong(uint)
    }

    public long intToUlong(int signedInt) {
        return signedInt; //TODO would this be correct?
    }

    public long intToLong(int signedInt) {
        return signedInt;
    }
}

或许这种风格在您的代码中更具可读性:

public class Uint {
    /** this class has only static utility methods and is not to be instantiated */
    private Uint() {
        super();
    }

    public static long toUlong(int uint) {
        return Convert.uintToUlong(uint);
    }
}

一些 JUnit 测试:

import static org.junit.Assert.assertEquals;

import org.junit.Test;

public class UintTest {
    @Test
    public void testToUlong() {
        long expected = Double.valueOf(Math.pow(2, 32) - 1).longValue();
        assertEquals("maxUint", expected, Uint.toUlong(-1));
        expected = Double.valueOf(Math.pow(2, 31)).longValue();
        assertEquals("minInt", expected, Uint.toUlong(Integer.MIN_VALUE));
        expected--;
        assertEquals("maxInt", expected, Uint.toUlong(Integer.MAX_VALUE));
        expected = 10000;
        assertEquals("10000", 10000l, Uint.toUlong(10000));
        expected = 3000000000l;
        assertEquals("3B", expected, Uint.toUlong(1500000000 + 1500000000));
    }
}

【讨论】:

  • 对于转换,我只使用long ulong = 0xFFFFFFFFL &amp; (long)uint。可怕的是,我第一次写这篇文章时不小心把L 关闭了。我立即将其拉入静态函数以防止再次发生这种情况。
  • @JasonN:那个static函数already exists
猜你喜欢
  • 1970-01-01
  • 2014-10-22
  • 2012-09-28
  • 2011-05-25
  • 1970-01-01
  • 2016-06-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多