41
class Test{  
    public static void main(String[] args){  
        float f1=3.2f;  
        float f2=6.5f;  

        if(f1==3.2){
            System.out.println("same");  
        }else{
            System.out.println("different");  
        }
        if(f2==6.5){
            System.out.println("same");  
        }else{  
            System.out.println("different");  
        }
    }  
}  

出力:

different
same

なぜそのような出力になるのですか?same私は最初のケースの結果として期待していました。

4

8 に答える 8

41

違いは、6.5 は float と double の両方で正確に表現できるのに対し、3.2 はどちらの型でも正確に表現できないことです。2 つの最も近い近似値が異なります。

float と double の等値比較では、最初に float を double に変換してから、2 つを比較します。したがって、データの損失。


float や double が等しいかどうかを比較するべきではありません。float または double に割り当てる数値が正確であることを実際に保証できないためです。

この丸め誤差は、浮動小数点演算の特徴です

無限に多くの実数を有限数のビットに圧縮するには、近似表現が必要です。無限に多くの整数がありますが、ほとんどのプログラムでは、整数計算の結果を 32 ビットで格納できます。

対照的に、ビット数が固定されている場合、実数を使用したほとんどの計算では、その数のビットを使用して正確に表現できない量が生成されます。したがって、浮動小数点計算の結果は、その有限表現に適合させるために丸めなければならないことがよくあります。この丸め誤差が浮動小数点演算の特徴です。

詳細については、すべてのコンピューター科学者が浮動小数点演算について知っておくべきことを確認してください。

于 2013-05-18T18:58:06.847 に答える
5

何を扱っているかを確認するには、FloatおよびDoubleの toHexString メソッドを使用できます。

class Test {
    public static void main(String[] args) {
        System.out.println("3.2F is: "+Float.toHexString(3.2F));
        System.out.println("3.2  is: "+Double.toHexString(3.2));
        System.out.println("6.5F is: "+Float.toHexString(6.5F));
        System.out.println("6.5  is: "+Double.toHexString(6.5));
    }
}
$ java Test
3.2F is: 0x1.99999ap1
3.2  is: 0x1.999999999999ap1
6.5F is: 0x1.ap2
6.5  is: 0x1.ap2

一般に、数値が A * 2^B に等しい場合、数値は正確な表現を持ちます。ここで、A と B は整数であり、その許容値は言語仕様によって設定されます (また、double にはより多くの許容値があります)。

この場合、
6.5 = 13/2 = (1+10/16)*4 = (1+a/16)*2^2 == 0x1.ap2、3.2
= 16/5 = ( 1 + 9/16 + 9/16^2 + 9/16^3 + . . . ) * 2^1 == 0x1.999. . . p1.
しかし、Java は有限の桁数しか保持できないため、.999 を切り捨てます。. . ある時点でオフ。(数学から 0.999. . .=1 を覚えているかもしれません。これは底 10 です。底 16 では、0.fff. . .=1 になります。)

于 2013-05-22T15:35:33.047 に答える
2

floattypeとの値をdouble直接比較することはできません。値を比較する前に、 を に変換するか、 を に変換する必要がdoubleあります。前者の比較を行う場合、変換は「 はの値の可能な限り最良の表現を保持しますか?」と尋ねます。後者の変換を行う場合、問題は「 はの値の完全な表現を保持するか」です。多くのコンテキストでは、前者の質問の方がより意味のある質問ですが、Java では、とのすべての比較は後者の質問をすることを意図していると想定しています。floatfloatdoublefloatfloatdoublefloatdoublefloatdouble

言語が許容するものに関係なく、コーディング標準では、型floatとのオペランド間の直接比較を絶対に積極的に禁止する必要があることをお勧めしますdouble。次のようなコードが与えられた場合:

float f = function1();
double d = function2();
...
if (d==f) ...

dで正確に表現できない値を が表す場合、どのような動作が意図されているかを伝えることは不可能floatです。fに変換することを意図doubleし、その変換の結果を と比較するd場合、比較を次のように記述する必要があります。

if (d==(double)f) ...

型変換によってコードの動作が変わることはありませんが、コードの動作が意図的なものであることは明らかです。の最適な表現をf保持しているかどうかを比較によって示すことが意図されている場合、次のようにする必要があります。floatd

if ((float)d==f)

この動作は、キャストなしで発生する動作とは大きく異なることに注意してください。元のコードdoubleが各比較のオペランドを にキャストしたfloat場合、両方の等価テストに合格します。

于 2013-12-20T22:41:56.137 に答える
1

一般に、近似の問題により、浮動小数点数で == 演算子を使用することはお勧めできません。

于 2013-05-18T18:58:41.277 に答える
0

6.5 はバイナリで正確に表現できますが、3.2 はできません。そのため、精度の違いは 6.5 では問題になりません6.5 == 6.5f

2 進数の仕組みをすばやく更新するには、次のようにします。

100 -> 4

10 -> 2

1 -> 1

0.1 -> 0.5 (or 1/2)

0.01 -> 0.25 (or 1/4)

6.5 バイナリ: 110.1(正確な結果、残りの桁はゼロのみ)

3.2 バイナリ: 11.001100110011001100110011001100110011001100110011001101...(ここでは精度が重要です!)

float の精度は 24 ビットのみです (残りは符号と指数に使用されます)。

3.2f バイナリ: 11.0011001100110011001100(倍精度近似とは異なります)

基本的には、1/5 と 1/7 を 10 進数で書く場合と同じです。

1/5 = 0,2
1,7 = 0,14285714285714285714285714285714.
于 2015-08-07T09:20:30.940 に答える