2

我创建了以下代码来测试 Float 和 Double Java 数字数据类型的下溢和上溢:

// Float Overflow & Underflow
float floatTest = Float.MAX_VALUE;
floatTest++;
out.println("Float Overflow: " + Float.MAX_VALUE + " + 1 = " + floatTest);
floatTest = Float.MIN_VALUE;
floatTest--;
out.println("Float Underflow: " + Float.MIN_VALUE + " - 1 = " + floatTest);
out.println("");

// Double Overflow & Underflow
double doubleTest = Double.MAX_VALUE;
doubleTest++;
out.println("Double Overflow: " + Double.MAX_VALUE + " + 1 = " + doubleTest);
doubleTest = Double.MIN_VALUE;
doubleTest--;
out.println("Double Underflow: " + Double.MIN_VALUE + " - 1 = " + doubleTest);
out.println("");

有人可以解释我在结果中看到的奇怪值: 在此处输入图像描述

当我使用 byte、short、int 和 long 进行类似的测试(下面的代码)时:

// BYTE Overflow & Underflow
byte byteTest = Byte.MAX_VALUE;
byteTest++;
out.println("Byte Overflow: " + Byte.MAX_VALUE + " + 1 = " + byteTest);
byteTest = Byte.MIN_VALUE;
byteTest--;
out.println("Byte Underflow: " + Byte.MIN_VALUE + " - 1 = " + byteTest);
out.println("");

// SHORT Overflow & Underflow
short shortTest = Short.MAX_VALUE;
shortTest++;
out.println("Short Overflow: " + Short.MAX_VALUE + " + 1 = " + shortTest);
shortTest = Short.MIN_VALUE;
shortTest--;
out.println("Short Underflow: " + Short.MIN_VALUE + " - 1 = " + shortTest);
out.println("");

// INTEGER Overflow & Underflow
int intTest = Integer.MAX_VALUE;
intTest++;
out.println("Integer Overflow: " + Integer.MAX_VALUE + " + 1 = " + intTest);
intTest = Integer.MIN_VALUE;
intTest--;
out.println("Integer Underflow: " + Integer.MIN_VALUE + " - 1 = " + intTest);
out.println("");

// LONG Overflow & Underflow
long longTest = Long.MAX_VALUE;
longTest++;
out.println("Long Overflow: " + Long.MAX_VALUE + " + 1 = " + longTest);
longTest = Long.MIN_VALUE;
longTest--;
out.println("Long Underflow: " + Long.MIN_VALUE + " - 1 = " + longTest);
out.println("");

结果看起来如预期:

在此处输入图像描述

有人可以解释 Java float 和 double 中的溢出和下溢吗?为什么我会看到上述结果?

4

3 回答 3

6

浮点溢出

添加1Double.MAX_VALUEORFloat.MAX_VALUE并不能代表足够的值来避免由于精度错误而被四舍五入。在处,由于尾数有 53 位,连续值之间Double.MAX_VALUE差异非常大。

System.out.println("Math.ulp(Double.MAX_VALUE) is " + Math.ulp(Double.MAX_VALUE));

1.9958403095347198E292

该值为 2 971

您需要添加一个表达式,该表达式至少产生这么多溢出到Infinity. 我说“至少产生这么多”,因为我可以通过添加 2 970来使其溢出,但 2 969没有效果。

doubleTest += Math.pow(2.0, 969);

1.7976931348623157E308

doubleTest += Math.pow(2.0, 970);

Infinity

看起来 2 970被四舍五入到 2 971以添加到Double.MAX_VALUE,但 2 969被四舍五入到0并且对总和没有影响。

发生了类似的过程float,尽管值几乎没有那么高。

浮点下溢

doubleTest = Double.MIN_VALUE;
doubleTest--;

这只是一个无穷小的值减一,实际上是减一。这不是下溢。

指数(而不是值)变得太低而无法表示时,就会发生下溢,因此0.0结果。除以2得到下溢。

doubleTest = Double.MIN_VALUE;
doubleTest /= 2;

0.0

整数/长上溢/下溢

这些是预期值,因为您知道值“环绕”到值范围的另一侧。

于 2018-01-22T19:23:13.933 回答
4

这些“奇怪”的结果并不是 Java 特有的。只是相关 IEEE 标准定义的浮点数比大多数人想象的要复杂得多。但是根据您的具体结果:Float.MIN_VALUE是最小的浮点数,因此非常接近 0。因此Float.MIN_VALUE - 1将非常接近 -1。但由于 -1 附近的浮点精度大于该差异,因此结果为 -1。至于Float.MAX_VALUE,这个值周围的浮点精度远大于 1 并且加一不会改变结果。

于 2018-01-22T19:17:13.503 回答
0

您的结果不一定与众不同,只是浮动通常非常具体并且您的要求非常接近。我在使用双数据类型时遇到了类似的问题。只需仔细检查以确保您的代码符合您的预期。

于 2018-01-22T19:59:56.737 回答