在实际应用之前,我们如何检查算术运算是否会超出数据类型的上限。
简称java中的上限是32767而我乘以328 * 100,我实际上无法与Short.MAX_VALUE
进行比较,因为在乘法后,答案已经溢出并且答案将是-32736,这肯定更小比Short.MAX_VALUE
再举一个例子,我说for int
在for循环中计算17 ^ 10(功率10为17)的值。我怎么知道我的答案在什么阶段溢出。
这个Short
和int
只是一个例子。想想这个问题,可以更深入地了解对所有数据类型可以做些什么。
我尝试使用谷歌搜索,但没有找到有助于理解这个概念的良好链接。
答案 0 :(得分:5)
有三种可能的溢出检查方法:
使用较大的类型并向下转换:将输入转换为下一个较大的原始整数类型,并以较大的大小执行算术运算。检查每个中间结果是否有原始较小类型的溢出;如果范围检查失败,则抛出ArithmeticException。
预检输入:检查每个算术运算符的输入,以确保不会发生溢出。如果执行操作会溢出,则再次抛出ArithmeticException,否则执行操作。
E.g:
static void preAddCheck(int left, int right) throws ArithmeticException {
if (right > 0 ? left > Integer.MAX_VALUE - right : left < Integer.MIN_VALUE - right) {
throw new ArithmeticException("Integer overflow");
}
}
BigInteger:将输入转换为BigInteger类型的对象,并使用BigInteger方法执行所有算术运算。溢出时抛出ArithmeticException。
答案 1 :(得分:4)
计划在Java 8的Math包中包含这些方法,但我不知道当前状态是什么。一些源代码可用here。我不知道如何测试实现,但这可以给你一些想法。
例如,int乘法是通过使用longs来完成的:
public static int multiplyExact(int x, int y) {
long r = (long)x * (long)y;
if ((int)r != r) {
throw new ArithmeticException("long overflow");
}
return (int)r;
}
但长乘法使用更复杂的算法:
public static long multiplyExact(long x, long y) {
long r = x * y;
long ax = Math.abs(x);
long ay = Math.abs(y);
if (((ax | ay) >>> 31 != 0)) {
// Some bits greater than 2^31 that might cause overflow
// Check the result using the divide operator
// and check for the special case of Long.MIN_VALUE * -1
if (((y != 0) && (r / y != x)) ||
(x == Long.MIN_VALUE && y == -1)) {
throw new ArithmeticException("long overflow");
}
}
return r;
}
答案 2 :(得分:2)
我使用最大可能类型BigInteger / BigDecimal进行计算。然后我根据它的大小将值分配给适当的类型......有趣的是,有一些有用的方法...如果值不能包含在short中,shortValueExtract将抛出ArithmetricException。
BigDecimal result = BigDecimal.valueOf(328).multiply(
BigDecimal.valueOf(100));
try {
short shortResult = result.shortValueExact();
} catch (ArithmeticException e) {
// overflow
System.out.println("Overflow!");
}
try {
int intResult = result.intValueExact();
} catch (ArithmeticException e) {
// overflow
}