このコードを単体テストで使用して、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;
}