Kus*_*alP 210 java integer integer-overflow
Java如何处理整数下溢和溢出?
在此之后,您将如何检查/测试这是否正在发生?
Bal*_*usC 204
如果它溢出,它会回到最小值并从那里继续.如果它下溢,它会回到最大值并从那里继续.
你可以事先检查一下如下:
public static boolean willAdditionOverflow(int left, int right) {
if (right < 0 && right != Integer.MIN_VALUE) {
return willSubtractionOverflow(left, -right);
} else {
return (~(left ^ right) & (left ^ (left + right))) < 0;
}
}
public static boolean willSubtractionOverflow(int left, int right) {
if (right < 0) {
return willAdditionOverflow(left, -right);
} else {
return ((left ^ right) & (left ^ (left - right))) < 0;
}
}
Run Code Online (Sandbox Code Playgroud)
(可以替代int由long执行相同的检查long)
如果您认为这可能会经常发生,那么请考虑使用可以存储更大值的数据类型或对象,例如long或者可能java.math.BigInteger.最后一个没有溢出,实际上,可用的JVM内存是限制.
如果您已经在Java8上,那么您可以使用新的Math#addExact()和Math#subtractExact()方法,这将导致ArithmeticException溢出.
public static boolean willAdditionOverflow(int left, int right) {
try {
Math.addExact(left, right);
return false;
} catch (ArithmeticException e) {
return true;
}
}
public static boolean willSubtractionOverflow(int left, int right) {
try {
Math.subtractExact(left, right);
return false;
} catch (ArithmeticException e) {
return true;
}
}
Run Code Online (Sandbox Code Playgroud)
当然,您也可以立即使用它们,而不是将它们隐藏在boolean实用程序方法中.
Dur*_*dal 65
好吧,就原始整数类型而言,Java根本不处理Over/Underflow(对于float和double,行为是不同的,它将像IEEE-754强制要求一样刷新到+/-无穷大).
添加两个int时,如果发生溢出,则不会显示任何指示.检查溢出的一种简单方法是使用下一个更大的类型来实际执行操作,并检查结果是否仍在源类型的范围内:
public int addWithOverflowCheck(int a, int b) {
// the cast of a is required, to make the + work with long precision,
// if we just added (a + b) the addition would use int precision and
// the result would be cast to long afterwards!
long result = ((long) a) + b;
if (result > Integer.MAX_VALUE) {
throw new RuntimeException("Overflow occured");
} else if (result < Integer.MIN_VALUE) {
throw new RuntimeException("Underflow occured");
}
// at this point we can safely cast back to int, we checked before
// that the value will be withing int's limits
return (int) result;
}
Run Code Online (Sandbox Code Playgroud)
你要做什么代替throw子句,取决于你的应用程序要求(throw,flush to min/max或者只记录任何东西).如果你想检测长时间操作的溢出,你就会对原语失去兴趣,而是使用BigInteger.
编辑(2014-05-21):由于这个问题似乎经常被提及而且我不得不自己解决同样的问题,因此用CPU计算其V标志的相同方法很容易评估溢出条件.
它基本上是一个布尔表达式,涉及两个操作数的符号以及结果:
/**
* Add two int's with overflow detection (r = s + d)
*/
public static int add(final int s, final int d) throws ArithmeticException {
int r = s + d;
if (((s & d & ~r) | (~s & ~d & r)) < 0)
throw new ArithmeticException("int overflow add(" + s + ", " + d + ")");
return r;
}
Run Code Online (Sandbox Code Playgroud)
在java中,将表达式(在if中)应用于整个32位更简单,并使用<0检查结果(这将有效地测试符号位).对于所有整数基元类型,该原理的作用完全相同,将上述方法中的所有声明更改为long使其长时间工作.
对于较小的类型,由于隐式转换为int(有关详细信息,请参阅JLS的按位操作),而不是检查<0,检查需要显式屏蔽符号位(短操作数为0x8000,字节操作数为0x80,调整强制转换)和参数声明适当地):
/**
* Subtract two short's with overflow detection (r = d - s)
*/
public static short sub(final short d, final short s) throws ArithmeticException {
int r = d - s;
if ((((~s & d & ~r) | (s & ~d & r)) & 0x8000) != 0)
throw new ArithmeticException("short overflow sub(" + s + ", " + d + ")");
return (short) r;
}
Run Code Online (Sandbox Code Playgroud)
(注意上面的例子使用表达式需要减去溢出检测)
那么这些布尔表达式如何/为什么有效?首先,一些逻辑思维揭示只有当两个参数的符号相同时才会发生溢出.因为,如果一个参数是负数且一个是正数,则结果(添加)必须接近零,或者在极端情况下,一个参数为零,与另一个参数相同.由于参数本身不能创建溢出条件,因此它们的总和也不能创建溢出.
那么如果两个参数具有相同的符号会发生什么呢?让我们看看两者都是正面的情况:添加两个创建大于MAX_VALUE类型的和的参数将始终产生负值,因此如果 arg1 + arg2> MAX_VALUE 则发生溢出.现在可能产生的最大值是MAX_VALUE + MAX_VALUE(极端情况下两个参数都是MAX_VALUE).对于一个字节(示例),这意味着127 + 127 = 254.查看可以通过添加两个正值导致的所有值的位表示,可以发现溢出的那些(128到254)都设置了第7位,而所有不溢出(0到127)都清除了第7位(最顶部,符号).这正是表达式的第一个(右)部分检查的内容:
if (((s & d & ~r) | (~s & ~d & r)) < 0)
Run Code Online (Sandbox Code Playgroud)
(~s&~d&r)变为真,只有当两个操作数(s,d)都为正且结果(r)为负时(表达式适用于所有32位,但我们唯一感兴趣的是是最顶部(符号)位,由<0)检查.
现在,如果两个参数都是负数,它们的总和永远不会比任何参数更接近零,则总和必须更接近负无穷大.我们可以产生的最极端值是MIN_VALUE + MIN_VALUE,它(再次用于字节示例)显示对于任何范围内的值(-1到-128),符号位被设置,而任何可能的溢出值(-129到-256) )符号位清零.因此结果的符号再次显示溢出情况.这就是左半边(s&d&r)检查两个参数(s,d)是否定的结果以及结果为正的情况.逻辑在很大程度上等同于积极的情况; 当且仅当发生下溢时,添加两个负值可能导致的所有位模式都将清除符号位.
Jim*_*Jim 32
对于int或long基本类型,Java不对整数溢出执行任何操作,而忽略具有正整数和负整数的溢出.
这个答案首先描述整数溢出,给出一个如何发生的例子,即使在表达式评估中使用中间值,然后给出资源的链接,这些资源提供了防止和检测整数溢出的详细技术.
整数算术和表达式导致意外或未检测到的溢出是一种常见的编程错误.意外或未检测到的整数溢出也是一个众所周知的可利用安全问题,尤其是它会影响数组,堆栈和列表对象.
溢出可以在正方向或负方向上发生,其中正值或负值将超出所讨论的基元类型的最大值或最小值.溢出可以在表达或操作评估期间在中间值中发生,并且影响表达或操作的结果,其中最终值将在预期在范围内.
有时负溢出被误称为下溢.下溢是当值接近零而不是表示允许时发生的情况.下溢发生在整数运算中并且是预期的.当整数评估介于-1和0或0和1之间时,会发生整数下溢.分数结果将截断为0.这是正常的,预期使用整数运算,不会被视为错误.但是,它可能导致代码抛出异常.如果整数下溢的结果用作表达式中的除数,则一个示例是"ArithmeticException:/ by zero"异常.
请考虑以下代码:
int bigValue = Integer.MAX_VALUE;
int x = bigValue * 2 / 5;
int y = bigValue / x;
Run Code Online (Sandbox Code Playgroud)
这导致x被赋值0并且随后对bigValue/x的评估抛出异常,"ArithmeticException:/ by zero"(即除以零),而不是y被赋值为2.
x的预期结果为858,993,458,小于最大int值2,147,483,647.但是,评估Integer.MAX_Value*2的中间结果将是4,294,967,294,它超过最大int值,并且根据2s补码整数表示为-2.随后的-2/5评估评估为0,并将其分配给x.
将用于计算x的表达式重新排列为一个表达式,在计算时,在乘以之前除以以下代码:
int bigValue = Integer.MAX_VALUE;
int x = bigValue / 5 * 2;
int y = bigValue / x;
Run Code Online (Sandbox Code Playgroud)
导致x被分配858,993,458,y被分配2,这是预期的.
bigValue/5的中间结果是429,496,729,它不超过int的最大值.对429,496,729*2的后续评估不超过int的最大值,并且预期结果被分配给x.对y的评估不会除以零.x和y的评估按预期工作.
Java整数值按照2s补码有符号整数表示存储和行为.当结果值大于或小于最大或最小整数值时,会产生2的补码整数值.在没有明确设计为使用2s补码行为的情况下,这是大多数普通的整数运算情况,得到的2s补码值将导致编程逻辑或计算错误,如上例所示.一篇优秀的维基百科文章在这里描述了2s 补码二进制整数:两个补码 - 维基百科
有一些技术可以避免无意的整数溢出.Techinques可以分类为使用前置条件测试,upcasting和BigInteger.
前置条件测试包括检查进入算术运算或表达式的值,以确保这些值不会发生溢出.编程和设计需要创建测试,以确保输入值不会导致溢出,然后确定如果发生将导致溢出的输入值该怎么做.
上传包括使用较大的基元类型来执行算术运算或表达式,然后确定结果值是否超出整数的最大值或最小值.即使使用向上转换,操作或表达式中的值或某个中间值仍可能超出upcast类型的最大值或最小值,并导致溢出,这也将无法检测到,并将导致意外和不希望的结果.通过分析或前提条件,可以防止在不进行预制或不实际预测的情况下进行预测时溢出.如果有问题的整数已经是长原始类型,那么Java中的原始类型无法进行向上转换.
BigInteger技术包括使用BigInteger进行算术运算或使用BigInteger的库方法表达式.BigInteger不会溢出.如有必要,它将使用所有可用内存.它的算术方法通常只比整数运算效率稍低.使用BigInteger的结果仍可能超出整数的最大值或最小值,但是,在导致结果的算术中不会发生溢出.如果BigInteger结果超出所需原始结果类型的最大值或最小值(例如int或long),编程和设计仍需要确定要执行的操作.
卡内基梅隆软件工程研究所的CERT计划和Oracle为安全Java编程创建了一套标准.标准中包括用于防止和检测整数溢出的技术.该标准在此处作为可免费访问的在线资源发布:CERT Oracle Java安全编码标准
描述和包含用于防止或检测整数溢出的编码技术的实际示例的标准部分在此处:NUM00-J.检测或防止整数溢出
还提供了CERT Oracle安全编码标准Java的书籍表格和PDF表格.
Jef*_*oom 32
默认情况下,Java的int和long数学在溢出和下溢时静默地循环.(对于其他整数类型的整数操作是通过首先根据JLS 4.2.2将操作数提升为int或long来执行的.)
从Java 8中,java.lang.Math提供了addExact,subtractExact,multiplyExact,incrementExact,decrementExact和negateExact两个执行指定的操作,溢出抛出ArithmeticException int和长期争论的静态方法.(没有divideExact方法 - 你必须自己检查一个特殊情况(MIN_VALUE / -1).)
从Java 8开始,java.lang.Math还提供toIntExact将long转换为int,如果long的值不适合int,则抛出ArithmeticException.这对于例如使用未经检查的长数学计算int的总和,然后使用toIntExact在结尾处转换为int(但注意不要让你的总和溢出)是有用的.
如果您仍在使用旧版本的Java,Google Guava会提供IntMath和LongMath静态方法,用于检查加法,减法,乘法和取幂(抛出溢出).这些类还提供了计算MAX_VALUE因溢出时返回的阶乘和二项式系数的方法(检查不太方便).番石榴的原始工具类,SignedBytes,UnsignedBytes,Shorts和Ints,提供checkedCast用于缩窄更大类型(上下溢/上溢,投掷抛出:IllegalArgumentException方法不 ArithmeticException),以及saturatingCast返回的方法MIN_VALUE或MAX_VALUE上溢.
fra*_*orl 12
我自己也遇到过这个问题,这是我的解决方案(对于乘法和加法):
static boolean wouldOverflowOccurwhenMultiplying(int a, int b) {
// If either a or b are Integer.MIN_VALUE, then multiplying by anything other than 0 or 1 will result in overflow
if (a == 0 || b == 0) {
return false;
} else if (a > 0 && b > 0) { // both positive, non zero
return a > Integer.MAX_VALUE / b;
} else if (b < 0 && a < 0) { // both negative, non zero
return a < Integer.MAX_VALUE / b;
} else { // exactly one of a,b is negative and one is positive, neither are zero
if (b > 0) { // this last if statements protects against Integer.MIN_VALUE / -1, which in itself causes overflow.
return a < Integer.MIN_VALUE / b;
} else { // a > 0
return b < Integer.MIN_VALUE / a;
}
}
}
boolean wouldOverflowOccurWhenAdding(int a, int b) {
if (a > 0 && b > 0) {
return a > Integer.MAX_VALUE - b;
} else if (a < 0 && b < 0) {
return a < Integer.MIN_VALUE - b;
}
return false;
}
Run Code Online (Sandbox Code Playgroud)
如果错误或者可以简化,请随时纠正.我已经使用乘法方法进行了一些测试,主要是边缘情况,但它仍然可能是错误的.
有些库提供安全的算术运算,可以检查整数溢出/下溢.例如,Guava的IntMath.checkedAdd(int a,int b)返回和的a和b,前提是它不溢出,ArithmeticException如果a + b有符号int算术溢出则抛出.
它包裹着.
例如:
public class Test {
public static void main(String[] args) {
int i = Integer.MAX_VALUE;
int j = Integer.MIN_VALUE;
System.out.println(i+1);
System.out.println(j-1);
}
}
Run Code Online (Sandbox Code Playgroud)
版画
-2147483648
2147483647
Run Code Online (Sandbox Code Playgroud)
我认为你应该使用这样的东西,它被称为Upcasting:
public int multiplyBy2(int x) throws ArithmeticException {
long result = 2 * (long) x;
if (result > Integer.MAX_VALUE || result < Integer.MIN_VALUE){
throw new ArithmeticException("Integer overflow");
}
return (int) result;
}
Run Code Online (Sandbox Code Playgroud)
您可以在此处进一步阅读: 检测或防止整数溢出
这是非常可靠的来源.
| 归档时间: |
|
| 查看次数: |
152927 次 |
| 最近记录: |