10

Java では、浮動小数点演算は正確に表現されません。たとえば、この Java コード:

float a = 1.2; 
float b= 3.0;
float c = a * b; 
if(c == 3.6){
    System.out.println("c is 3.6");
} 
else {
    System.out.println("c is not 3.6");
} 

「c は 3.6 ではありません」と表示されます。

小数点以下 3 桁 (#.###) を超える精度には興味がありません。フロートを乗算して確実に比較するために、この問題にどのように対処できますか?

4

9 に答える 9

19

浮動小数点数は(a==b) のように比較するべきではなく(Math.abs(a-b) < delta)deltaが小さな数値である場合のように比較するのが原則です。

10 進形式の固定桁数を持つ浮動小数点値は、2 進形式の固定桁数を持つ必要はありません。

明確にするための追加:

浮動小数点数の厳密な==比較はほとんど実用的な意味がありませんが、逆に、厳密な<and>比較は有効なユース ケースです (例 - 特定の値がしきい値を超えたときにトリガーされるロジック: (val > threshold) && panic();) 。

于 2010-05-24T09:51:47.947 に答える
7

固定精度の数値に関心がある場合は、 のようなBigDecimal本質的に近似する (高精度ですが) 型ではなく、 のような固定精度型を使用する必要がありますfloat。スタック オーバーフローには、多くの言語でこれをより詳細に説明する類似の質問が多数あります。

于 2010-05-24T09:43:50.977 に答える
6

Java とは何の関係もないと思います。IEEE 754 浮動小数点数で発生します。これは、浮動小数点表現の性質によるものです。IEEE 754 形式を使用するすべての言語で、同じ問題が発生します。

上記のDavidが提案したように、java.lang.Mathクラスのメソッドabsを使用して絶対値を取得する必要があります(正/負の符号を削除します)。

これを読むことができます: http://en.wikipedia.org/wiki/IEEE_754_revisionまた、優れた数値法の教科書は問題に十分に対処します。

public static void main(String[] args) {
    float a = 1.2f;
    float b = 3.0f;
    float c = a * b;
        final float PRECISION_LEVEL = 0.001f;
    if(Math.abs(c - 3.6f) < PRECISION_LEVEL) {
        System.out.println("c is 3.6");
    } else {
        System.out.println("c is not 3.6");
    }
}
于 2010-05-24T10:11:54.170 に答える
3

このコードを単体テストで使用して、2 つの異なる計算の結果が同じであるかどうかを比較し、浮動小数点演算エラーを除きます。

これは、浮動小数点数のバイナリ表現を調べることで機能します。複雑さのほとんどは、浮動小数点数の符号が 2 の補数ではないという事実によるものです。それを補正した後、基本的に単純な減算で ULP の差が得られます (以下のコメントで説明されています)。

/**
 * Compare two floating points for equality within a margin of error.
 * 
 * This can be used to compensate for inequality caused by accumulated
 * floating point math errors.
 * 
 * The error margin is specified in ULPs (units of least precision).
 * A one-ULP difference means there are no representable floats in between.
 * E.g. 0f and 1.4e-45f are one ULP apart. So are -6.1340704f and -6.13407f.
 * Depending on the number of calculations involved, typically a margin of
 * 1-5 ULPs should be enough.
 * 
 * @param expected The expected value.
 * @param actual The actual value.
 * @param maxUlps The maximum difference in ULPs.
 * @return Whether they are equal or not.
 */
public static boolean compareFloatEquals(float expected, float actual, int maxUlps) {
    int expectedBits = Float.floatToIntBits(expected) < 0 ? 0x80000000 - Float.floatToIntBits(expected) : Float.floatToIntBits(expected);
    int actualBits = Float.floatToIntBits(actual) < 0 ? 0x80000000 - Float.floatToIntBits(actual) : Float.floatToIntBits(actual);
    int difference = expectedBits > actualBits ? expectedBits - actualBits : actualBits - expectedBits;

    return !Float.isNaN(expected) && !Float.isNaN(actual) && difference <= maxUlps;
}

double精度浮動小数点のバージョンは次のとおりです。

/**
 * Compare two double precision floats for equality within a margin of error.
 * 
 * @param expected The expected value.
 * @param actual The actual value.
 * @param maxUlps The maximum difference in ULPs.
 * @return Whether they are equal or not.
 * @see Utils#compareFloatEquals(float, float, int)
 */
public static boolean compareDoubleEquals(double expected, double actual, long maxUlps) {
    long expectedBits = Double.doubleToLongBits(expected) < 0 ? 0x8000000000000000L - Double.doubleToLongBits(expected) : Double.doubleToLongBits(expected);
    long actualBits = Double.doubleToLongBits(actual) < 0 ? 0x8000000000000000L - Double.doubleToLongBits(actual) : Double.doubleToLongBits(actual);
    long difference = expectedBits > actualBits ? expectedBits - actualBits : actualBits - expectedBits;

    return !Double.isNaN(expected) && !Double.isNaN(actual) && difference <= maxUlps;
}
于 2013-07-26T01:20:33.233 に答える
2

これはすべての浮動小数点表現の弱点であり、10 進法では固定数の小数を持っているように見えるいくつかの数値が、実際には 2 進法では無限の数の小数を持っているために発生します。あなたが 1.2 だと思っているのは、実際には 1.199999999997 のようなものです。これを 2 進数で表すと、特定の数の後の小数点以下を切り捨てなければならず、精度がいくらか失われるからです。それを 3 倍すると、実際には 3.5999999 になります...

http://docs.python.org/py3k/tutorial/floatingpoint.html <-これはそれをよりよく説明するかもしれません(たとえそれがpythonの場合でも、浮動小数点表現の一般的な問題です)

于 2010-05-24T10:14:05.397 に答える
2

double を比較するための apache クラスがあります: org.apache.commons.math3.util.Precision

これには、いくつかの興味深い定数が含まれています:SAFE_MINおよびEPSILONは、算術演算を実行するときに可能な最大偏差です。

また、double を比較、均等化、または丸めるために必要なメソッドも提供します。

于 2015-05-19T11:24:31.363 に答える
2

他の人が書いたように:

float を次のものと比較します。if (Math.abs(a - b) < delta)

これを行うための素敵なメソッドを書くことができます:

public static int compareFloats(float f1, float f2, float delta)
{
    if (Math.abs(f1 - f2) < delta)
    {
         return 0;
    } else
    {
        if (f1 < f2)
        {
            return -1;
        } else {
            return 1;
        }
    }
}

/**
 * Uses <code>0.001f</code> for delta.
 */
public static int compareFloats(float f1, float f2)
{
     return compareFloats(f1, f2, 0.001f);
}

したがって、次のように使用できます。

if (compareFloats(a * b, 3.6f) == 0)
{
    System.out.println("They are equal");
}
else
{
    System.out.println("They aren't equal");
}
于 2010-05-24T10:46:57.407 に答える
-1

2 つのフロートf1を比較するには、次のようにする必要があると思います。f2#.###

((int) (f1 * 1000 + 0.5)) == ((int) (f2 * 1000 + 0.5))

f1 * 1000に持ち上げる3.14159265...3141.59265+ 0.5結果は に3142.09265なり(int)、小数点以下は切り捨てられ3142ます。つまり、小数点以下 3 桁を含み、最後の桁を適切に丸めます。

于 2010-05-24T10:12:38.363 に答える