Java Top.

开始使用Spring 5和Spring Boot 2,通过学习春天课程:

>>查看课程

1.介绍

在本教程中,我们将研究Java中数值数据类型的溢出和下溢出。

我们不会深入研究更理论化的方面——我们只关注在Java中发生的时间。

首先,我们将查看整数数据类型,然后查看浮点数据类型。对于这两者来说,我们还会看到我们如何检测到何时发生过量或欠溢。

2.溢出和下溢

只需放置,溢出和下溢时会在为变量的已声明的数据类型范围超出范围的值时发生。

如果(绝对)值太大,我们将其升级溢出,如果值太小,我们称之为下溢。

让我们来看看我们尝试分配价值的示例101000(一种11000zeros)到类型的变量或者双倍的。这个价值太大了或者双倍的变量,就会出现溢出。

第二个例子,假设我们试图赋值10-1000年(非常接近0)到类型的变量双倍的。这个值太小而不是双倍的在Java中变量,将有底部流。

让我们更详细地看看Java在这些情况下会发生什么。

3.整数数据类型

Java中的整数数据类型是字节(8位),短的(16位),(32位),和(64位)。

这里,我们将关注数据类型。相同的行为适用于其他数据类型,除了最小值和最大值不同。

类型的整数在Java中可以是负面的或正面的,这意味着它的32位,我们可以分配值之间的值-231(-2147483648) 和231-1(2147483647)。

包装类整数定义两个保存这些值的常量:integer.min_value.integer.max_value.

3.1。例子

如果我们定义变量会发生什么类型的并尝试分配一个太大的值(例如,21474836478 = max_value + 1)?

这个赋值的一个可能结果是将是未定义的,或者会出现错误。

两者都是有效的结果;但是,在Java中,价值-2147483648(最小值)。另一方面,如果我们试图赋值为-2147483649 (= min_value - 1),2147483647(最大值)。这种行为称为整数环绕。

让我们考虑下面的代码片段来更好地演示这个行为:

int value = Integer.MAX_VALUE-1;For (int I = 0;我< 4;i++, value++) {System.out.println(value);}

我们将获得以下输出,介绍溢出:

2147483646 2147483647 -2147483648 -2147483647

4.处理下溢和整数数据类型的溢出

当溢出发生时,Java不会抛出异常;这就是为什么它很难找到由溢出产生的错误。我们也不能直接访问溢出标志,这在大多数cpu中都可用。

但是,有各种方法可以处理可能的溢出。让我们看看几种这些可能性。

4.1。使用不同的数据类型

如果我们想允许值大于2147483647(或小于-2147483648),我们可以简单地使用数据类型或aBigInteger代替。

虽然类型的变量也可以溢出,最小值和最大值要大得多,在大多数情况下可能足够了。

价值范围BigInteger不受限制,除了JVM可用的内存量。

让我们看看如何重写上面的例子BigInteger:

biginteger smallvalue = new biginteger(integer.max_value +“”);for(int i = 0; i <4; i ++){system.out.println(stailvalue);sallutvalue = sallyvalue.add(biginteger.one);}

我们将看到以下输出:

2147483647 2147483648 2147483649 2147483649 2147483640 2147483650

我们可以在输出中看到,这里没有溢出。我们的文章大二世BigInteger在Java中盖子BigInteger更多细节。

4.2。抛出一个例外

有些情况我们不想允许更大的值,也不希望发生溢出,而且我们希望抛出异常。

正如Java 8的那样,我们可以使用精确算术运算的方法。让我们先看看一个例子:

int value = Integer.MAX_VALUE-1;for(int i = 0; i <4; i ++){system.out.println(值);value = math.addexact(值,1);}

静态方法addExact ()执行正常添加,但如果操作导致溢出或下溢则抛出异常:

2147483646 2147483646线程“main”java.lang.arithmeticexception:java.lang.math.addexact的整数溢出(Math.java:790)在Baeldung.oderflow.Overundlem.Main(Overu金宝搏188体育nderFlow.java:115)

除了addExact (),数学包为所有算术操作提供了相应的精确方法。看到Java文档获取所有这些方法的列表。

此外,还有一些精确转换方法,如果在转换到另一个数据类型期间出现溢出,这些方法将抛出异常。

从a转换对A.:

public static int toIntExact(long a)

以及从BigInteger对A.或者:

BigInteger largeValue = BigInteger. ten;long longValue = largeValue.longValueExact();int intValue = largeValue.intValueExact();

4.3。在Java 8之前

将确切的算术方法添加到Java 8中。如果我们使用早期版本,我们可以自行创建这些方法。这样做的一个选项是实现与Java 8中相同的方法:

公共静态int andexact(int x,int y){int r = x + y;if(((x ^ r)&(y ^ r))<0){投掷新的arithmeticexception(“int overflow”);}返回r;}

5.非整数数据类型

非整数类型漂浮双倍的当涉及算术操作时,不要以与整数数据类型相同的方式行为。

一个不同的是浮点数上的算术运算可能导致a。我们有一个专门的文章南在Java中,所以我们不会在本文中进一步介绍。此外,没有确切的算术方法,例如addExact或者multiplyExact中的非整数类型数学包中。

Java跟随java浮点算法的IEEE标准(IEEE 754)因为它漂浮双倍的数据类型。本标准是Java处理浮点数的缺点和下溢的方式的基础。

在下面几节中,我们将重点讨论双倍的数据类型,以及我们如何处理它们发生的情况。

5.1。溢出

至于整数数据类型,我们可能会期望:

asserttrue(double.max_value + 1 == double.min_value);

但是,浮点变量不是这种情况。以下是真的:

assertTrue(双。MAX_VALUE + 1 ==双值);

这是因为一个双倍的价值只有有限数量的有效位。如果我们增加一个大的价值双倍的值仅为1时,我们不改变任何有效位。因此,该值保持不变。

如果我们增加了我们变量的值,使我们增加变量的一个有效位之一,则变量将具有值:

assertTrue(双。MAX_VALUE * 2 == Double.POSITIVE_INFINITY);

NEGATIVE_INFINITY为负值时:

assertTrue(双。MAX_VALUE * -2 == Double.NEGATIVE_INFINITY);

我们可以看到,与整数不同的是,这里没有环绕,但是溢出有两种不同的可能结果:值保持不变,或者我们得到一个特殊值,POSITIVE_INFINITY或者NEGATIVE_INFINITY

5.2。下溢

有两个常数为最小值定义双倍的值:min_value.(4.9 e - 324)MIN_NORMAL(2.2250738585072014 e - 308)。

浮点算法的IEEE标准(IEEE 754)详细解释了这些细节的细节。

让我们专注于为什么我们根本需要最小值的浮点数。

一个双倍的Value不能任意小,因为我们只有有限的位数来表示值。

章节金宝搏官网188be类型、值和变量在Java SE语言规范中,描述了如何表示浮点类型。二进制表示的最小指数双倍的被给出-1074年。这意味着一个双倍的最小阳性值是math.pow(2,-1074),等于4.9 e - 324

因此,a双倍的在Java中不支持0之间的值4.9e-324,或者-4.9e-324.0为负值。

那么,如果我们试图将一个太小的值赋给一个类型变量会发生什么呢双倍的吗?让我们来看一个例子:

for(int i = 1073; i <= 1076; i ++){system.out.println(“2 ^”+ i + i +“=”+ math.pow(2,-i));}

输出:

2 ^ 1073 = 1.0e-323 2 ^ 1074 = 4.9e-324 2 ^ 1075 = 0.0 = 0.0 ^ 1076 = 0.0

我们看到,如果赋值太小,就会得到下溢,结果是0.0(积极的零)。
类似地,对于负值,下溢将导致值-0.0(负0)。

6.检测浮点数据类型的下溢和溢出

由于溢出将导致正无穷大,并且在正面或负零中的下溢,我们不需要精确的算术方法,例如整数数据类型。相反,我们可以检查这些特殊常量来检测过度和下溢。

如果我们想在这种情况下抛出异常,我们可以实施辅助方法。让我们来看看如何寻找指数:

公共静态双重POWExact(双基础,双指数){if(base == 0.0){返回0.0;双重结果= math.pow(基本,指数);if(结果== double.positive_infinity){抛出新的arithmeticexception(“导致正面的双重溢出”);}如果(结果== double.negation_infinity){抛出新的arithmeticexception(“overnal_infinity导致的双重溢出”);}如果(double.compare(-0.0f,结果)== 0){抛出新的arithmeticexception(“导致负零导致的双溢出”);}否则如果(double.compare(+ 0.0f,结果)== 0){投掷新的arithmeticexception(“导致正零导致的双溢出”);返回结果;}

在此方法中,我们需要使用该方法double.compare()。正常的比较运算符(<>)不要区分正负零。

7.积极的和消极的

最后,让我们看一个例子来说明为什么在处理正、负零和无穷时需要小心。

让我们定义几个变量来展示:

Double a = +0f;Double b = -0f;

因为积极和消极0被认为是平等的:

断言(a == b);

而正无穷大和负无穷大被认为是不同的:

asserttrue(1 / a == double.positive_infinity);asserttrue(1 / b == double.negation_infinity);

但是,以下断言是正确的:

assertTrue (1 / ! = 1 / b);

这似乎是我们第一次断言的矛盾。

结论

在本文中,我们了解了什么是溢出和下溢出,它如何在Java中发生,以及整数和浮点数据类型之间的区别。

我们还了解了如何在程序执行期间检测溢出和下溢出。

像往常一样,完整的源代码可用在github上

Java底部

开始使用Spring 5和Spring Boot 2,通过学习春天课程:

>>查看课程
评论在本文上关闭!