为什么将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标准甚至其他实现的上下文中。
它位于JLS中,请参阅: JavaRanchpost http://www.coderanch.com/t/239753/java-programmer-SCJP/certification/cast-double-int但是警告会很好。
Java在这方面很明显,但修复它可能会破坏现有代码。
试图通过深入研究IEEE或Java规范来certificate这种行为是正确的。 1/0抛出一个算术exception,Math.round(Float.NaN)也应该抛出。
(这是最小惊讶的原则。)