有趣的NaN類型


  在學習Java集合的時候遇到了Float.isNaN(float)函數,點進去一看就不理解了,函數實現如下:

public static boolean isNaN(float v) {
        return (v != v);
    }

  float的v怎么會不等於自身呢?以下是關於這個函數的描述:

   /**
     * Returns {@code true} if the specified number is a
     * Not-a-Number (NaN) value, {@code false} otherwise.
     *
     * @param   v   the value to be tested.
     * @return  {@code true} if the argument is NaN;
     *          {@code false} otherwise.
     */
isNaN()
描述:返回一個 Boolean 值,指明提供的值是否是保留值 NaN (不是數字)。
語法:isNaN(numvalue)numvalue 參數是要檢查是否為 NaN 的值。
說明:如果值是 NaN, 那么 isNaN 函數返回 true ,否則返回 false 。

  從上述描述可以知道,這個函數判斷一個float參數是不是NaN(Not a Number),即不是一個數字,那么什么是NaN,什么情況下會出現NaN呢?


定義:  

  NaN(Not a Number,非數)是計算機科學中數值數據類型的一個值,表示未定義或不可表示的值。常在浮點數運算中使用。首次引入NaN的是1985年的IEEE 754浮點數標准。

會返回NaN的運算:

  • 操作數中至少有一個是 NaN 的運算
  • 未定義操作
    • 下列除法運算:0/0、∞/∞、∞/−∞、−∞/∞、−∞/−∞
    • 下列乘法運算:0×∞、0×-∞
    • 下列加法運算:∞ + (−∞)、(−∞) + ∞
    • 下列減法運算:∞ - ∞、(−∞) - (−∞)
  • 產生復數結果的實數運算。例如:
    • 對負數進行開方運算
    • 對負數進行對數運算
    • 對比-1小或比+1大的數進行反正弦或反余弦運算

 

Java中的NaN:

  Java虛擬機在處理浮點數運算時,不會拋出任何運行時異常(這里所講的是java語言中的異常,請勿與IEEE 754規范中的浮點異常相互混淆,IEEE 754的浮點異常是一種運算信號),當一個操作產生溢出時,將會使用有符號的無窮大來表示,如果某個操作結果沒有明確的數學定義的話,將會使用NaN值來表示,所有使用NaN值作為操作數的算術操作,結果都返回NaN。Java中的Double和Float中都有isNaN函數,判斷一個數是不是NaN,其實現都是通過上述 v != v 的方式,因為NaN是唯一與自己不相等的值,NaN與任何值都不相等。有些操作會使isNaN返回True,例如:

System.out.println(Float.isNaN(0.0f / 0.0f)); System.out.println(Double.isNaN(Math.sqrt(-1)));

Double的NaN定義:

   /**
     * A constant holding a Not-a-Number (NaN) value of type
     * {@code double}. It is equivalent to the value returned by
     * {@code Double.longBitsToDouble(0x7ff8000000000000L)}.
     */
    public static final double NaN = 0.0d / 0.0;

  以上定義了一個常量型的NaN,它的效果和Double.longBitsToDouble(0x7ff8000000000000L)的返回值是一樣的,我們可以看看Double.longBitsToDouble中的定義:

If the argument is any value in the range 0x7ff0000000000001L through 0x7fffffffffffffffL or in the range 0xfff0000000000001L through 0xffffffffffffffffL, the result is a NaN. No IEEE 754 floating-point operation provided by Java can distinguish between two NaN values of the same type with different bit patterns.

  如上可知,二進制的0x7ff0000000000001L~0x7fffffffffffffffL 和 0xfff0000000000001L~0xffffffffffffffffL 之間的數值是被定義成NaN類型,類似正無窮大和負無窮大,但又有區別。在Float中也類似。

  總之,在實際數值計算的過程中可能會出現一些非法的操作,導致產生了非法的數值,比如說除零,通過NaN來表示這樣一個非數值,NaN與任何浮點數(包括自身)的比較結果都為假,即 (NaN ≠ x) = false,這是NaN獨有的特性,所以可以使用與自己相比來確定當前數值是不是一個正常的數。

簡單測試:

  @Test
    public void NanTest() {
        Float f1 = new Float(-1.0 / 0.0);
        Float f2 = new Float(0.0 / 0.0);
        Double f3 = Math.sqrt(-1);

        // returns true if this Float value is a Not-a-Number(NaN), else false
        System.out.println(f1 + " = " + f1.isNaN());
        System.out.println(f2 + " = " + f2.isNaN());
        System.out.println(f3 + " = " + f3.isNaN());
        System.out.println(Double.isNaN(Double.longBitsToDouble(0x7ff0000000000011L)));
    }

 再看看這邊的解釋:Double.NaN != Double.NaN

  還有一點需要注意:在將一個float的NaN窄化轉換為整數類型T(T限於int或long類型之一)的時候,那轉換結果就是int或long類型的0;而double類型的NaN值將按規定轉換為float類型的NaN值。

 

 

參考資料:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM