为什么将Double.NaN转换为int而不是在Java中抛出exception?

所以我知道IEEE 754为非实数的值指定了一些特殊的浮点值。 在Java中,将这些值转换为原始int不会像我期望的那样抛出exception。 相反,我们有以下内容:

 int n; n = (int)Double.NaN; // n == 0 n = (int)Double.POSITIVE_INFINITY; // n == Integer.MAX_VALUE n = (int)Double.NEGATIVE_INFINITY; // n == Integer.MIN_VALUE 

在这些情况下抛出exception的理由是什么? 这是IEEE标准,还是仅仅是Java设计者的选择? 如果这种演员阵容有例外情况,我是否会发现不良后果?

在这些情况下不抛出exception的理由是什么?

我想这个原因包括:

  • 这些是边缘情况,很可能在执行此类操作的应用程序中很少发生。

  • 这种行为并非“完全出乎意料”。

  • 当应用程序从double转换为int时,预计会出现重大的信息丢失。 该应用程序要么忽略这种可能性,要么演员之前将进行检查以防止它…这也可以检查这些情况。

  • 没有其他双重/浮动操作会导致exception,而(IMO)在这种情况下执行它会有点精神分裂。

这是IEEE标准,还是仅仅是Java设计者的选择?

我认为后者。

如果这种演员阵容有例外情况,我是否会发现不良后果?

没有明显的……

(但它并不是真正相关.JLS和JVM规范说出了他们所说的内容,而更改它们将会破坏现有代码。而且我们现在谈论的不只是Java代码……)


我做了一些挖掘。 许多可以使用的x86指令从double转换为整数似乎生成硬件中断……除非被屏蔽。 (对我而言)不清楚指定的Java行为是否比OP建议的替代方案更容易或更难实现。

在这些情况下不抛出exception的理由是什么? 这是IEEE标准,还是仅仅是Java设计者的选择?

第20页和第21页的IEEE 754-1985标准在2.2.1 NANs和2.2.2 Infinity部分下清楚地解释了标准要求NAN和Infinity值的原因。 因此这不是Java的事情。

3.8.1浮点运算和IEEE 754中的Java虚拟机规范指出,当执行到整数类型的转换时,JVM将向零舍入,这将解释您看到的结果。

该标准确实提到了一个名为“陷阱处理程序”的function,可用于确定何时发生溢出或NAN,但Java虚拟机规范明确指出这不是针对Java实现的。 它在第3.8.1节中说:

Java虚拟机的浮点操作不会抛出exception,陷阱或以其他方式发出IEEE 754无效操作exception条件,除零,溢出,下溢或不精确的信号。 Java虚拟机没有信令NaN值。

因此,无论后果如何,行为都没有明确规定。

如果这种演员阵容有例外情况,我是否会发现不良后果?

理解标准中陈述的理由应该足以回答这个问题。 该标准用详尽的例子解释了你在这里要求的后果。 我会发布它们,但这里的信息太多了,在这个版本工具中无法正确格式化示例。

编辑

我正在阅读JCP最近发布的Java虚拟机规范的最新维护评论,作为他们在JSR 924上的工作的一部分,在2.11.14节中,命名类型转换结构包含一些可以帮助您寻求的更多信息。答案,还不是你想要的,但我相信它有点帮助。 它说:

在将浮点值缩小到数值转换为整数类型T(其中T为int或long)时,浮点值将按如下方式转换:

  • 如果浮点值为NaN,则转换结果为a
    int或long 0。
  • 否则,如果浮点值不是无穷大,则
    浮点值四舍五入到
    使用IEEE 754的整数值V.
    向零模式转。

有两种情况:

  • 如果T很长并且这个整数值可以表示为long,那么
    结果是长值V.
  • 如果T的类型为int,并且此整数值可以表示为int,则结果为int值V.

除此以外:

  • 值必须太小(大幅度或负无穷大的负值),结果是int或long类型的最小可表示值。
  • 或者值必须太大(大幅度的正值或
    积极的无限性)和结果
    是int或long类型的最大可表示值。

从double到float的缩小数字转换符合IEEE 754.使用IEEE 754 round到最近模式正确舍入结果。 太小而不能表示为float的值将转换为float类型的正零或负零; 太大而不能表示为float的值将转换为正或负无穷大。 双NaN总是转换为浮子NaN。

尽管可能发生溢出,下溢或精度损失,但缩小数值类型之间的转换不会导致Java虚拟机抛出运行时exception(不要与IEEE 754浮点exception混淆)。

我知道这只是重述你已经知道的东西,但它有一个线索,看来IEEE标准要求四舍五入到最近。 也许在那里你可以找到这种行为的原因。

编辑

第2.3.2节“舍入模式状态”中讨论的IEEE标准:

默认情况下,舍入表示向最近的方向舍入。 该标准要求提供其他三种舍入模式; 即向0舍入,向+无限圆并向无穷大舍入。

当与转换为整数运算一起使用时,舍入为-Infinity会导致转换成为floor函数,而舍入+ Infinity则为ceiling。

模式舍入会影响溢出,因为当向O方向舍入或向-Infinite有效时,正幅度溢出会导致默认结果为最大可表示数字,而不是+无穷大。

类似地,负向量的溢出将在向+无穷大或向O向着圆的情况下产生最大的负数。

然后他们继续提到一个为什么这在区间运算中有用的例子。 不确定,这是您正在寻找的答案,但它可以丰富您的搜索。

实际上我认为在某些演员阵容中有一些位操作(可能是针对性能问题?),这样你就可以有一些意想不到的行为。 看看使用>>和<<运算符时会发生什么。

举个例子:

 public static void main(String[] args) { short test1 = (short)Integer.MAX_VALUE; System.out.println(test1); short test2 = (short)Integer.MAX_VALUE-1; System.out.println(test2); short test3 = (short)Integer.MAX_VALUE-2; System.out.println(test3); short test4 = (short)Double.MAX_VALUE-3; System.out.println(test4); } 

将输出:

 -1 -2 -3 -4 

1998年有一个ACM演示文稿仍然看起来令人惊讶,并带来了一些亮点: https : //people.eecs.berkeley.edu/~wkahan/JAVAhurt.pdf 。

更具体地说,关于在投射NaN和无穷大时出乎意料的缺乏例外:参见第3页,第3点:“没有保护浮点陷阱和IEEE标准754/854强制要求的标志释放的无穷大和NaN相信Java声称具有鲁棒性。 “

该演示文稿并未真正回答“为什么”,但确实解释了Java语言浮点实现中有问题的设计决策的后果,并将它们置于IEEE标准甚至其他实现的上下文中。

Java在这方面很明显,但修复它可能会破坏现有代码。

试图通过深入研究IEEE或Java规范来certificate这种行为是正确的。 1/0抛出一个算术exception,Math.round(Float.NaN)也应该抛出。

(这是最小惊讶的原则。)