有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

Java 8中处理“无符号”整数的注释思想

我正在编写与硬件和数学相关的代码,并广泛使用32位和64位无符号整数值。没有任何javac支持,bug一直很稳定,很难解决。Java8在装箱的Long类上添加了一些用于除法、mod和比较的函数,但尽管这些函数提供了运行时支持,但由于缺少编译时警告,无法将这两个函数意外地混合在一起,这让我非常恼火

有人知道有什么方法可以帮助解决这些问题吗?Java团队的一名成员提到possible annotation support用于类型检查

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

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

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

希望任何想法都不会对运行时产生影响,所以我负担不起在类或bignums中包装unsigned。好吧,如果我不需要任何分配或线程局部变量的开销,我可能能够将它们封装在一个类中,但这可能会使代码不可重入。对于简单的热点代码,也可以将其包装为不带符号的整数。(事实上,我认为Hotspot可能足够聪明,可以靠近——可能需要额外的内存读写。)

其他人是如何围绕这个问题编码的


共 (2) 个答案

  1. # 1 楼答案

    编辑:截至2016年7月,Checker框架附带了您需要的大部分或全部内容:Signedness Checker,它验证有符号和无符号值的一致使用。从manual开始:

    The Signedness Checker guarantees that signed and unsigned values are not mixed together in a computation. In addition, it prohibits meaningless operations, such as division on an unsigned value.

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


    旧的答案如下:

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

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

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

  2. # 2 楼答案

    在检测方面,我没有对这些进行过太多的研究,但我会从this answer中查看Lint4j或其他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));
        }
    }