仅为浮点数据类型定义POSITIVE_INFINITY,NEGATIVE_INFINITY,NaN常量的目的,但不为整数数据类型定义[重复]

问题描述 投票:15回答:4

我想了解为什么POSITIVE_INFINITYNEGATIVE_INFINITY常量只定义为浮点数据类型(floatdouble及其包装器),

public static final float POSITIVE_INFINITY = 1.0f / 0.0f;
public static final float NEGATIVE_INFINITY = -1.0f / 0.0f;

但不适用于整数数据类型(byteshortintlong及其包装)。这会影响不同数据类型的除法运算结果。例如:

对于整数类型:

int z = 10/0;
System.out.println(z);

Output:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at TesterClass.main(TesterClass.java:16)

对于浮点类型:

double z = 10/0.0;
System.out.println(z);

Output:
Infinity
java
4个回答
26
投票

Java中的整数类型使用无符号二进制(对于char)或二进制补码表示。在这些表示中都没有“无限”的表示。例如,使用int有2 ^ 32个可能的值,并且它们都代表有限数。

Integer.MIN_VALUE是-231,Integer.MAX_VALUE是231 - 1,如果你把它们全部计算在内......包括零......这会产生232个不同的值。)

相比之下,使用IEEE二进制浮点表示来表示浮点数,并且这些数字确实具有表示无穷大和非数字值的标准方式。

因此,为浮点类型定义POSITIVE_INFINITYNEGATIVE_INFINITY常量是有意义的,并且不可能为整数类型定义它们。


如果你想知道为什么会这样:

  • 设计/选择整数表示(很久以前!)以最大化速度。任何特殊情况(如保留表示无穷大的值等)都会使整数算术硬件更复杂,更慢。如果硬件设计者的目标是在一个时钟周期内进行整数加法,那么使加法更加复杂意味着时钟速度必须更慢。这会影响整个处理器的速度。 另一方面是: 在没有任何明确通知的情况下发生溢出(可能需要也可能不需要) 除零必须通过硬件异常来处理,这会导致主要的性能损失......如果它确实发生了。
  • 设计IEEE浮点表示的标准委员会也考虑了需要能够代表无限的科学和工程领域的要求。由于需要进行缩放等,浮点运算已经变得更慢,更复杂。因此,它们很可能已经是多周期指令,并且处理特殊情况可能存在一些“松弛”。 此外,还有一个优点:INF和NaN值允许创建它们的操作在没有硬件异常的情况下继续进行,但没有像在整数溢出一样“扫除地毯下的不良操作”。

请注意,1949年(EDSAC)在工作计算机中使用了两个补码。 IEEE 754标准于1985年出现。


对于它的价值,一些编程语言意识到整数溢出;例如阿达。但是他们不会用无穷大的表示等来做这件事。相反,当操作溢出时,它们会抛出异常(或等效的)。即便如此,这也增加了性能损失,因为溢出检测通常需要在每个整数算术指令之后的额外指令来测试“溢出”状态位。 (这就是现代教学设定的方式......)


6
投票

它是IEEE 754浮点标准的一部分,如this spec中所述:

浮点类型是floatdouble,它们在概念上与单精度32位和双精度64位格式IEEE 754值和操作相关联,如IEEE标准二进制浮点运算,ANSI / IEEE标准754-1985(IEEE,纽约)。

IEEE 754标准不仅包括由符号和幅度组成的正数和负数,还包括正和负零,正和负无穷大以及特殊的非数字值(以下简称为NaN)。

这些特殊值是根据标准的位表示计算的。例如,基于Double位表示计算0x7ff0000000000000正无穷大。

相反,整数类型没有无限值的位表示。它们只有有限数的表示。 Integer类将最小和最大有限值定义为-231和231-1。


3
投票

正如其他人所指出的,它是在IEEE规范等中。浮点数和双精度数支持NaN和Infinity,而整数则不支持。

就其背后的推理而言,没有任何东西可以被零整除,而对于整数,你知道你试图除以零。

浮点数不准确。 0.003f - 0.001f - 0.002f在数学上为零,但根据IEEE规范和我们在计算机中表示数字的能力,它是-2.3283064E-10。您可以用二进制表示有限数量的十进制数,并且没有任何表示可以让我们始终获得零的正确值。

如果tinyFloat ==(0.003f - 0.001f - 0.002f)== -2.3283064E-10

这在数学上为零,几乎为零,但是1f / tinyFloat == -4.2949673E9

// This still works too:
scala> Integer.MAX_VALUE / (tinyFloat * tinyFloat * tinyFloat)
res58: Float = -1.7014118E38

// But eventually you overflow
scala> Integer.MAX_VALUE / (tinyFloat * tinyFloat * tinyFloat * tinyFloat)
res59: Float = Infinity

(如果您不熟悉,Scala是一种JVM语言,因此上述值类型与Java相同。)

最后一个tinyFloat ^ 4仍然不完全为零,因此计算机抛出ArithmeticException没有意义。整数不存在此问题。没有其他方法可以溢出分裂。 Integer.MAX_VALUE / 1仍为Integer.MAX_VALUE。你要么除以零,这在数学上是无效的,可用二进制表示,或者你没有,并得到了有效的结果。


1
投票

数学上,1/0 = Infinity因为我们认为除以零作为limit(x -> 0) 1/x,而且隐含的理解x是一个正数量。浮点除法必须支持小数除法,并且假设操作在语义上有一个小的误差范围,并且假设该操作可能包括0,那么由于四舍五入导致由于小数量的除法而引发异常完全等于0是完全不合适的。

除以整数零除以实际上是未定义的。如果你除以整数零,你可能不应该。当然,做这些额外的事情会严重影响性能。

为了比较,Integer支持Integer.MAX/MIN_VALUE。这些不应该由于操作而产生,而是用于像int minSoFar = Integer.MAX_VALUE;之类的步骤的算法。

© www.soinside.com 2019 - 2024. All rights reserved.