1. 程式人生 > >從干將莫邪的故事說起--java比較操作注意要點

從干將莫邪的故事說起--java比較操作注意要點

故事背景

《搜神記》:

 

楚干將、莫邪為楚王作劍,三年乃成。王怒,欲殺之。劍有雌雄。其妻重身當產。夫語妻曰:“吾為王作劍,三年乃成。王怒,往必殺我。汝若生子是男,大,告之曰:‘出戶望南山,鬆生石上,劍在其背。’”於是即將雌劍往見楚王。王大怒,使相之:“劍有二,一雄一雌,雌來雄不來。”王怒,即殺之。

莫邪子名赤,比後壯,乃問其母曰:“吾父所在?”母曰:“汝父為楚王作劍,三年乃成。王怒殺之。去時囑我:‘語汝子,出戶望南山,鬆生石上,劍在其背。’”於是子出戶南望,不見有山,但睹(dû)堂前鬆柱下石低之上。即以斧破其背,得劍,日夜思欲報楚王。

王夢見一兒,眉間廣尺,言欲報仇。王即購之千金。兒聞之,亡去,入山行歌。客有逢者,謂:“子年少,何哭之甚悲耶(yé)?”曰:“吾干將、莫邪子也,楚王殺吾父,吾欲報之!”客曰:“聞王購子頭千金,將子頭與劍來,為子報之。”兒曰:“幸甚!”即自刎(wên),兩手捧頭及劍奉之,立僵。客曰:“不負子也。”於是屍乃僕。

客持頭往見楚王,王大喜。客曰:“此乃勇士頭也,當於湯鑊(huò)煮之。”王如其言。煮頭三日三夕,不爛,頭踔出湯中,瞋目大怒。客曰:“此兒頭不爛,願王自往臨視之,是必爛也。”王即臨之。客以劍擬王,王頭隨墜湯中,客亦自擬己頭,頭復墜湯中。三首俱爛,不可識辨。乃分其湯肉葬之,故通名“三王墓”,今在汝南北宜春縣界。

java中的雌雄雙劍0與-0

 

0與-0按理說都是一樣的,但在程式中不一定就相等哦。請看下面的程式:

    public static void main(String[] args) {
        System.out.println(Float.compare(-0.0f, 0.0f));
        System.out.println(Math.max(-0.0f, 0.0f));
        System.out.println(Math.min(-0.0f, 0.0));
        System.out.println(-0.0f<0.0f);
        System.out.println(-0.0f<=0.0f);
    }

上面的結果可能讓你大吃一驚。

-1
0.0
-0.0
false
true

讓我們看看JSL上怎麼說吧

15.20.1. Numerical Comparison Operators <, <=, >, and >=
The type of each of the operands of a numerical comparison operator must be a type that is convertible (§5.1.8) to a primitive numeric type, or a compile-time error occurs.
Binary numeric promotion is performed on the operands (§5.6.2).
Note that binary numeric promotion performs value set conversion (§5.1.13) and may perform unboxing conversion (§5.1.8).
If the promoted type of the operands is int or long, then signed integer comparison is performed.
If the promoted type is float or double, then floating-point comparison is performed.
Comparison is carried out accurately on floating-point values, no matter what value sets their representing values were drawn from.
The result of a floating-point comparison, as determined by the specification of the IEEE 754 standard, is:
If either operand is NaN, then the result is false.
All values other than NaN are ordered, with negative infinity less than all finite values, and positive infinity greater than all finite values.
Positive zero and negative zero are considered equal.
For example, -0.0<0.0 is false, but -0.0<=0.0 is true.
Note, however, that the methods Math.min and Math.max treat negative zero as being strictly smaller than positive zero.
Subject to these considerations for floating-point numbers, the following rules then hold for integer operands or for floating-point operands other than NaN:
The value produced by the < operator is true if the value of the left-hand operand is less than the value of the right-hand operand, and otherwise is false.
The value produced by the <= operator is true if the value of the left-hand operand is less than or equal to the value of the right-hand operand, and otherwise is false.
The value produced by the > operator is true if the value of the left-hand operand is greater than the value of the right-hand operand, and otherwise is false.
The value produced by the >= operator is true if the value of the left-hand operand is greater than or equal to the value of the right-hand operand, and otherwise is false.

 

Positive zero and negative zero are considered equal.(浮點數中0.0和-0.0進行關係比較是相等的)

For example, -0.0<0.0 is false, but -0.0<=0.0 is true.(例如:-0.0<0.0是false,但-0.0<=0.0 是true)

Note, however, that the methods Math.min and Math.max treat negative zero as being strictly smaller than positive zero.(主意:然而,使用Math.min和Math.max進行浮點數比較時,-0.0時小於0.0的)

java中浮點數在迴圈中使用要慎重

    public static void main(String[] args) {
        final int START = 2000000000;
        int count = 0;
        for (float f = START; f < START + 50; f++)
        count++;
        System.out.println(count);
        }

f的初始值接近於Integer.MAX_VALUE,因此它需要用31 位來精確表示,而float 型別只能提供24 位的精度。對如此巨大的一個float 數值進行增量操作將不會改變其值。因此,這個程式看起來應該無限地迴圈下去,因為f 永遠也不可能解決其終止值。但是,如果你執行該程式,就會發現它並沒有無限迴圈下去,事實上,它立即就終止了,並打印出0。怎麼回事呢?

問題在於終止條件測試失敗了,其方式與增量操作失敗的方式非常相似。這個迴圈只有在迴圈索引f 比(float)(START + 50)小的情況下才執行。在將一個int與一個float 進行比較時,會自動執行從int 到float 的提升[JLS 15.20.1]。遺憾的是,這種提升是會導致精度丟失的三種拓寬原始型別轉換的一種[JLS5.1.2]。(另外兩個是從long 到float 和從long 到double。)

f 的初始值太大了,以至於在對其加上50,然後將結果轉型為float 時,所產生的數值等於直接將f 轉換成float 的數值。換句話說,(float)2000000000 ==2000000050,因此表示式f < START + 50 即使是在迴圈體第一次執行之前就是false,所以,迴圈體也就永遠的不到機會去執行。

總結

資料比較的時候,儘量避開浮點數,使用int,long等。

參考資料

【1】https://baike.baidu.com/item/%E5%B9%B2%E5%B0%86%E8%8E%AB%E9%82%AA/550371?fr=aladdin

【2】https://docs.oracle.com/javase/specs/jls/se12/html/jls-15.html#jls-15.