簡體   English   中英

為什么將 Double.NaN 轉換為 int 不會在 Java 中引發異常?

[英]Why does casting Double.NaN to int not throw an exception in Java?

所以我知道 IEEE 754 為非實數值指定了一些特殊的浮點值。 在 Java 中,將這些值轉換為原始int不會像我預期的那樣拋出異常。 相反,我們有以下內容:

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

在這些情況下拋出異常的理由是什么? 這是一個 IEEE 標准,還是僅僅是 Java 的設計者的選擇? 如果這樣的演員陣容可能出現異常,是否會出現我不知道的不良后果?

在這些情況下不拋出異常的理由是什么?

我想原因包括:

  • 這些是邊緣情況,在執行此類事情的應用程序中可能很少發生。

  • 這種行為並非“完全出乎意料”。

  • 當應用程序從 double 轉換為 int 時,預計會丟失大量信息。 應用程序要么會忽略這種可能性,要么會在演員表之前進行檢查以防止它發生......這也可以檢查這些情況。

  • 沒有其他雙/浮點操作會導致異常,並且(IMO)在這種情況下這樣做會有點精神分裂。

  • 在某些硬件平台(當前或未來)上可能會受到性能影響。

一位評論員這樣說:

“我懷疑不讓轉換引發異常的決定是出於強烈希望避免出於任何原因引發異常的強烈願望,因為害怕強制代碼將其添加到 throws 子句中。”

我不認為這是一個合理的解釋:

  • Java 語言設計者1沒有“出於任何原因”避免拋出異常的心態。 Java API 中有許多示例證明了這一點。

  • throws子句的問題通過不檢查異常來解決。 實際上,許多相關異常(如ArithmeticExceptionClassCastException )都因此被聲明為未檢查。

這是一個 IEEE 標准,還是僅僅是 Java 的設計者的選擇?

后者,我認為。

如果這樣的演員陣容可能出現異常,是否會出現我不知道的不良后果?

除了明顯的,沒有...

(但這並不真正相關。JLS 和 JVM 規范說明了他們所說的內容,更改它們可能會破壞現有代碼。我們現在談論的不僅僅是 Java 代碼......)


我做了一些挖掘工作。 許多可用於從雙精度轉換為整數的 x86 指令似乎會產生硬件中斷......除非被屏蔽。 (對我來說)不清楚指定的 Java 行為是否比 OP 建議的替代方案更容易或更難實現。


1 - 我不否認一些 Java 程序員確實有這種想法。 但他們是/不是 Java 設計師,這個問題專門詢問 Java 設計原理。

在這些情況下不拋出異常的理由是什么? 這是一個 IEEE 標准,還是僅僅是 Java 的設計者的選擇?

IEEE 754-1985標准在第 20 頁和第 21 頁的 2.2.1 NAN 和 2.2.2 Infinity 部分下清楚地解釋了標准要求 NAN 和 Infinity 值的原因。 因此,這不是 Java 的事情。

Java 虛擬機規范在第3.8.1 節浮點算術和 IEEE 754 中指出,當轉換為整數類型時,JVM 將應用向零舍入,這解釋了您所看到的結果。

該標准確實提到了一個名為“陷阱處理程序”的功能,該功能可用於確定何時發生溢出或 NAN,但 Java 虛擬機規范明確指出這不適用於 Java。 它在第 3.8.1 節中說:

Java 虛擬機的浮點運算不會引發異常、陷阱或以其他方式向 IEEE 754 異常條件發出信號,即無效運算、被零除、溢出、下溢或不精確。 Java 虛擬機沒有信令 NaN 值。

因此,無論后果如何,行為都不是未指定的。

如果這樣的演員陣容可能出現異常,是否會出現我不知道的不良后果?

了解標准中所述的原因應該足以回答這個問題。 該標准通過詳盡的示例解釋了您在此處要求的后果。 我會發布它們,但是這里的信息太多,並且這些示例可能無法在此版本工具中正確格式化。

編輯

我正在閱讀 JCP 最近發布的Java 虛擬機規范的最新維護評論,這是他們在JSR 924上工作的一部分,並且在 2.11.14 節命名類型轉換指令中包含更多信息,可以幫助您尋求答案,還不是您要查找的內容,但我相信它會有所幫助。 它說:

在浮點值到整數類型 T 的窄數值轉換中,其中 T 為 int 或 long,浮點值轉換如下:

  • 如果浮點值為 NaN,則轉換結果為
    整數或長 0。
  • 否則,如果浮點值不是無窮大,則
    浮點值四舍五入為
    使用 IEEE 754 的 integer 值 V
    向零模式舍入。

有兩種情況:

  • 如果 T 是長的並且這個 integer 值可以表示為長,那么
    結果是長值 V。
  • 如果 T 是 int 類型,並且這個 integer 值可以表示為 int,則結果是 int 值 V。

否則:

  • 該值必須太小(大的負值或負無窮大),結果是 int 或 long 類型的最小可表示值。
  • 或者該值必須太大(一個很大的正值或
    正無窮大),結果
    是 int 或 long 類型的最大可表示值。

從 double 到 float 的縮小數字轉換的行為符合 IEEE 754。使用 IEEE 754 舍入到最接近的模式正確舍入結果。 太小而不能表示為浮點數的值將轉換為浮點類型的正零或負零; 太大而無法表示為浮點數的值將轉換為正無窮大或負無窮大。 雙精度 NaN 始終轉換為浮點 NaN。

盡管可能會發生上溢、下溢或精度損失,但縮小數字類型之間的轉換不會導致 Java 虛擬機拋出運行時異常(不要與 IEEE 754 浮點異常混淆)。

我知道這只是重申了您已經知道的內容,但它有一個線索,似乎 IEEE 標准要求四舍五入到最接近的值。 也許在那里你可以找到這種行為的原因。

編輯

第 2.3.2 節舍入模式中討論的 IEEE 標准狀態:

默認情況下,舍入意味着向最接近的方向舍入。 該標准要求提供其他三種舍入模式; 即向 0 舍入,向 +Infinity 舍入,向 -Infinity 舍入。

當與轉換為整數運算一起使用時,向 -Infinity 舍入會使轉換成為地板函數,而向 +Infinity 舍入是天花板。

模式舍入會影響溢出,因為當朝 O 舍入或朝 -Infinite 舍入有效時,正幅度的溢出會導致默認結果為最大可表示數字,而不是 +Infinity。

類似地,當向 +Infinity 舍入或向 O 舍入有效時,負量級的溢出將產生最大的負數。

然后他們繼續提到一個例子,說明為什么這在區間算術中很有用。 再次不確定這是否是您正在尋找的答案,但它可以豐富您的搜索。

有一個 1998 年的 ACM 演示文稿似乎仍然令人驚訝地更新並帶來了一些亮點: https://people.eecs.berkeley.edu/~wkahan/JAVAhurt.pdf

更具體地說,關於在轉換 NaN 和無窮大時出人意料地缺乏異常:請參見第 3 頁,第 3 點:“在沒有 IEEE 標准 754/854 規定的浮點陷阱和標志保護的情況下釋放的無窮大和 NaN 與 Java 聲稱的健壯性相悖。 "

該演示文稿並沒有真正回答“為什么”,但確實解釋了 Java 語言的浮點實現中存在問題的設計決策的后果,並將它們置於 IEEE 標准甚至其他實現的上下文中。

它在 JLS 中,請參閱:JavaRanch 帖子http://www.coderanch.com/t/239753/java-programmer-SCJP/certification/cast-double-int但是警告會很好。

實際上,我認為在某些鑄造過程中會完成一些位操作(可能是因為性能問題?),這樣您就可以有一些意想不到的行為。 看看使用 >> 和 << 運算符時會發生什么。

舉個例子:

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);
}

將 output:

-1
-2
-3
-4

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM