处理 Java 8 中 "unsigned" 整数的想法
Ideas to deal with "unsigned" integers in Java 8
我正在编写与硬件和数学相关的代码,并广泛使用 32 位和 64 位无符号整数值。在没有任何 javac 支持的情况下,错误一直很稳定且难以解决。 Java 8 在 boxed Long class 上添加了一些函数用于除法、mod 和比较,但是虽然这些提供了 运行-time 支持,但缺少编译时关于两者意外混合的警告让我想拔头发。
有人知道有什么方法可以帮助解决这些问题吗? Java 团队成员之一提到 possible annotation support 进行类型检查。
到目前为止,我在所有无符号变量前加上 u_,我什至尝试在每次出现应无符号的 int
或 long
之前添加一个 /* unsigned */
注释。虽然这些很有用,但它们也非常混乱。这还不够。犯错的机会实在是太多了。
两个较大的问题是不需要的符号扩展和混合运算。
像 /* unsigned */ long u_lowmask = 0xffffffff
这样无伤大雅的东西并没有达到预期的结果,甚至只是 /* unsigned */ long u_widen64 = small32
就毁了那个无声扩展的东西。在其他语言中,我可以从编译器或类似 Lint 的静态检查器获得警告。函数式语言通常将其构建到类型检查机制中,但 Java 避开了这些,转而采用其他解决方案。混合比较或操作的机会太多了。
任何想法都应该没有 运行 时间影响,所以我无法承受将 unsigned 包装在 class 或 bignums 中。好吧,如果我可以在没有任何分配或线程本地开销的情况下完成它,我也许可以将它们包装在 class 中,但这可能会使代码不可重入。此外,Hotspot 还必须能够为接近简单本机的包装无符号整数发出代码。 (实际上,我认为 Hotspot 可能足够聪明,可以接近 - 可能是额外的内存读写。)
其他人如何围绕这个问题进行编码?
在检测端,我没有研究太多,但我会检查一下Lint4j or other Java lint tools from this answer。
理想情况下,该工具会在每次将 long
变量分配给 int
值时向您发出警告,然后您可以使用如下解决方案将每个此类分配包装在静态(可内联)方法调用(从而删除警告)。
对于赋值静态包装器方法,像这样的实用程序 class 过去对我很有效:
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));
}
}
编辑:截至 2016 年 7 月,Checker Framework 附带了您所要求的大部分或全部内容:Signedness Checker, which verifies consistent use of signed vs. unsigned values. From the 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 Framework 附带 20 type-checkers, but not one for unsigned arithmetic. You would need to write your own type-checker。这应该相对简单,因为类型检查器需要一些特殊规则:您只是不想混合有符号和无符号值。
有关需要库注释的 JDK 方法的列表,请参阅
http://hg.openjdk.java.net/jdk8/tl/jdk/rev/71200c517524
我正在编写与硬件和数学相关的代码,并广泛使用 32 位和 64 位无符号整数值。在没有任何 javac 支持的情况下,错误一直很稳定且难以解决。 Java 8 在 boxed Long class 上添加了一些函数用于除法、mod 和比较,但是虽然这些提供了 运行-time 支持,但缺少编译时关于两者意外混合的警告让我想拔头发。
有人知道有什么方法可以帮助解决这些问题吗? Java 团队成员之一提到 possible annotation support 进行类型检查。
到目前为止,我在所有无符号变量前加上 u_,我什至尝试在每次出现应无符号的 int
或 long
之前添加一个 /* unsigned */
注释。虽然这些很有用,但它们也非常混乱。这还不够。犯错的机会实在是太多了。
两个较大的问题是不需要的符号扩展和混合运算。
像 /* unsigned */ long u_lowmask = 0xffffffff
这样无伤大雅的东西并没有达到预期的结果,甚至只是 /* unsigned */ long u_widen64 = small32
就毁了那个无声扩展的东西。在其他语言中,我可以从编译器或类似 Lint 的静态检查器获得警告。函数式语言通常将其构建到类型检查机制中,但 Java 避开了这些,转而采用其他解决方案。混合比较或操作的机会太多了。
任何想法都应该没有 运行 时间影响,所以我无法承受将 unsigned 包装在 class 或 bignums 中。好吧,如果我可以在没有任何分配或线程本地开销的情况下完成它,我也许可以将它们包装在 class 中,但这可能会使代码不可重入。此外,Hotspot 还必须能够为接近简单本机的包装无符号整数发出代码。 (实际上,我认为 Hotspot 可能足够聪明,可以接近 - 可能是额外的内存读写。)
其他人如何围绕这个问题进行编码?
在检测端,我没有研究太多,但我会检查一下Lint4j or other Java lint tools from this answer。
理想情况下,该工具会在每次将 long
变量分配给 int
值时向您发出警告,然后您可以使用如下解决方案将每个此类分配包装在静态(可内联)方法调用(从而删除警告)。
对于赋值静态包装器方法,像这样的实用程序 class 过去对我很有效:
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));
}
}
编辑:截至 2016 年 7 月,Checker Framework 附带了您所要求的大部分或全部内容:Signedness Checker, which verifies consistent use of signed vs. unsigned values. From the 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 Framework 附带 20 type-checkers, but not one for unsigned arithmetic. You would need to write your own type-checker。这应该相对简单,因为类型检查器需要一些特殊规则:您只是不想混合有符号和无符号值。
有关需要库注释的 JDK 方法的列表,请参阅 http://hg.openjdk.java.net/jdk8/tl/jdk/rev/71200c517524