“处理问题的想法”;“未签名”;Java8中的整数
我正在编写与硬件和数学相关的代码,并广泛使用32位和64位无符号整数值。没有任何javac支持,bug一直很稳定,很难解决。Java8在装箱的Long类上添加了一些用于除法、mod和比较的函数,但尽管这些函数提供了运行时支持,但由于缺少编译时警告,无法将这两个函数意外地混合在一起,这让我非常恼火 有人知道有什么方法可以帮助解决这些问题吗?Java团队成员之一提到了类型检查 到目前为止,我在所有无符号变量的前面加上u_,甚至在每次出现应无符号的“处理问题的想法”;“未签名”;Java8中的整数,java,annotations,java-8,unsigned,Java,Annotations,Java 8,Unsigned,我正在编写与硬件和数学相关的代码,并广泛使用32位和64位无符号整数值。没有任何javac支持,bug一直很稳定,很难解决。Java8在装箱的Long类上添加了一些用于除法、mod和比较的函数,但尽管这些函数提供了运行时支持,但由于缺少编译时警告,无法将这两个函数意外地混合在一起,这让我非常恼火 有人知道有什么方法可以帮助解决这些问题吗?Java团队成员之一提到了类型检查 到目前为止,我在所有无符号变量的前面加上u_,甚至在每次出现应无符号的int或long之前添加/*unsigned*/注释。
int
或long
之前添加/*unsigned*/
注释。虽然这些是有用的,但它们也非常杂乱。这还不够。犯错误的机会太多了
两个较大的问题是不需要的符号扩展和混合操作
像/*unsigned*/long u_lowmask=0xffffffff
这样无害的东西并没有得到想要的结果,或者甚至只是/*unsigned*/long u_wide64=small32
破坏了无提示扩展的功能。在其他语言中,我可以从编译器或类似Lint的静态检查器获得警告。函数式语言通常将其构建到类型检查机制中,但Java已经避开了这些机制,转而采用其他解决方案。有太多机会进行混合比较或操作
希望任何想法都不会对运行时产生影响,所以我负担不起在类或bignums中包装unsigned。好吧,如果我不需要任何分配或线程局部变量的开销,我可能能够将它们封装在一个类中,但这可能会使代码不可重入。Hotspot还必须能够为接近简单本机的包装无符号整数发出代码。(事实上,我认为Hotspot可能足够聪明,可以靠近——可能需要额外的内存读写。)
其他人是如何围绕这个问题编写代码的?在检测方面,我没有对这些进行过太多的研究,但我会从中查看Java lint或其他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;
}
}
一些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));
}
}
编辑:截至2016年7月,Checker框架附带了您要求的大部分或全部内容:验证有符号值与无符号值的一致使用。从: 有符号性检查器保证有符号值和无符号值在计算中不会混合在一起。此外,它还禁止无意义的操作,例如对无符号值进行除法 (向@MAGx2致敬,因为他注意到这个答案已经过时了。)
旧的答案如下: 我建议你调查一下这件事。它使您能够定义自己的类型限定符批注,例如
@Unsigned
,然后在编译时检查代码对于这些批注的类型是否正确。如果它没有发出警告,那么您可以保证代码不会混合使用有符号和无符号值
Checker框架附带了,但没有一个用于无符号算术。你需要这样做。这应该是相对简单的,因为类型检查器需要很少的特殊规则:您只是不想混合使用有符号值和无符号值
有关需要库注释的JDK方法的列表,请参阅
重写以删除对库或工具的任何引用。我认为,基于注释的解决方案将是最干净的,但目前还没有合适的检查工具。对于转换,我只使用
long ulong=0xFFFFFFFFL&(long)uint
。糟糕的是,我第一次写这篇文章时,不小心把L
关掉了。我立即将其放入一个静态函数中,以防止再次出现这种情况。@JasonN:thatstatic
函数这可能是我将要得到的最接近的函数了。我会调查的。这也可能是一个很好的例子,说明为什么这里的一些规则写得很差,遵守得很差。我花了四次重写这个问题——所有之前的问题都被关闭,要求提供一个库或工具的建议——最终得到一个答案,这是一个库或工具。在一个开发人员的语言中有另一个开发人员的库。我发现在Checker框架中有对已签名/未签名的检查
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));
}
}