53

Java で 2 つの double を使用して等しいか、より小さいか、より大きいかをテストするクラスを作成しました。私の一般的なケースは、0.5 セントの精度を持つ価格を比較することです。59.395 と比較して 59.005。私が選択したイプシロンは、これらのケースに適していますか?

private final static double EPSILON = 0.00001;


/**
 * Returns true if two doubles are considered equal.  Tests if the absolute
 * difference between two doubles has a difference less then .00001.   This
 * should be fine when comparing prices, because prices have a precision of
 * .001.
 *
 * @param a double to compare.
 * @param b double to compare.
 * @return true true if two doubles are considered equal.
 */
public static boolean equals(double a, double b){
    return a == b ? true : Math.abs(a - b) < EPSILON;
}


/**
 * Returns true if two doubles are considered equal. Tests if the absolute
 * difference between the two doubles has a difference less then a given
 * double (epsilon). Determining the given epsilon is highly dependant on the
 * precision of the doubles that are being compared.
 *
 * @param a double to compare.
 * @param b double to compare
 * @param epsilon double which is compared to the absolute difference of two
 * doubles to determine if they are equal.
 * @return true if a is considered equal to b.
 */
public static boolean equals(double a, double b, double epsilon){
    return a == b ? true : Math.abs(a - b) < epsilon;
}


/**
 * Returns true if the first double is considered greater than the second
 * double.  Test if the difference of first minus second is greater then
 * .00001.  This should be fine when comparing prices, because prices have a
 * precision of .001.
 *
 * @param a first double
 * @param b second double
 * @return true if the first double is considered greater than the second
 *              double
 */
public static boolean greaterThan(double a, double b){
    return greaterThan(a, b, EPSILON);
}


/**
 * Returns true if the first double is considered greater than the second
 * double.  Test if the difference of first minus second is greater then
 * a given double (epsilon).  Determining the given epsilon is highly
 * dependant on the precision of the doubles that are being compared.
 *
 * @param a first double
 * @param b second double
 * @return true if the first double is considered greater than the second
 *              double
 */
public static boolean greaterThan(double a, double b, double epsilon){
    return a - b > epsilon;
}


/**
 * Returns true if the first double is considered less than the second
 * double.  Test if the difference of second minus first is greater then
 * .00001.  This should be fine when comparing prices, because prices have a
 * precision of .001.
 *
 * @param a first double
 * @param b second double
 * @return true if the first double is considered less than the second
 *              double
 */
public static boolean lessThan(double a, double b){
    return lessThan(a, b, EPSILON);
}


/**
 * Returns true if the first double is considered less than the second
 * double.  Test if the difference of second minus first is greater then
 * a given double (epsilon).  Determining the given epsilon is highly
 * dependant on the precision of the doubles that are being compared.
 *
 * @param a first double
 * @param b second double
 * @return true if the first double is considered less than the second
 *              double
 */
public static boolean lessThan(double a, double b, double epsilon){
    return b - a > epsilon;
}
4

9 に答える 9

112

お金を表すために double を使用しないでください。決して。java.math.BigDecimal代わりに使用してください。

次に、丸めを行う方法を正確に指定することができ (これは、金融アプリケーションでは法律で規定されている場合があります!)、このイプシロンのような愚かなハックを行う必要はありません。

真剣に、浮動小数点型を使用してお金を表すことは、非常に専門的ではありません。

于 2008-12-10T17:24:11.250 に答える
16

はい。Java double は、与えられた 0.00001 のイプシロンよりも精度が高くなります。

浮動小数点値の格納が原因で発生する丸め誤差は、0.00001 より小さくなります。私は定期的に1E-6or 0.000001 を Java の二重イプシロンに問題なく使用しています。

これに関連して、私は の形式が好きです。epsilon = 1E-5;なぜなら、より読みやすいからです (Java の 1E-5 = 1 x 10^-5)。1E-6 はコードを読むと 1E-5 と簡単に区別できますが、0.00001 と 0.000001 はコードをざっと見ると非常に似ているため、同じ値だと思います。

于 2008-12-10T17:15:12.797 に答える
11

おっおっおっ。通貨に浮動小数点を使用している特定の理由はありますか?それとも、任意精度の固定小数点数形式を使用したほうがよいでしょうか? あなたが解決しようとしている具体的な問題が何であるかはわかりませんが、0.5 セントが本当に扱いたいものなのか、それとも不正確な数値形式を使用した結果にすぎないのかを考える必要があります。

于 2008-12-10T17:21:13.517 に答える
6

BigDecimal を使用できる場合はそれを使用し、それ以外の場合:

/**
  *@param precision number of decimal digits
  */
public static boolean areEqualDouble(double a, double b, int precision) {
   return Math.abs(a - b) <= Math.pow(10, -precision);
}
于 2014-02-04T17:06:49.193 に答える
6

お金を扱っている場合は、Money 設計パターンを確認することをお勧めします (元は、Martin Fowler のエンタープライズ アーキテクチャ設計に関する本から)。

動機については、このリンクを読むことをお勧めします: http://wiki.moredesignpatterns.com/space/Value+Object+Motivation+v2

于 2008-12-10T17:30:29.053 に答える
2

double がお金に悪いという考えには同意しますが、それでも double を比較するという考えには興味深いものがあります。特に、提案されているイプシロンの使用は、特定の範囲の数値にのみ適しています。以下は、2 つの数値の比率に対するイプシロンのより一般的な使用法です (0 のテストは省略されています)。

boolean equal(double d1, double d2) {
  double d = d1 / d2;
  return (Math.abs(d - 1.0) < 0.001);
}
于 2010-10-21T19:09:10.103 に答える
1

浮動小数点数の有効桁数は非常に多くなりますが、それよりもはるかに大きくなる可能性があります。アプリで大きな数を処理する場合は、イプシロンの値が異なる必要があることに気付くでしょう。

0.001+0.001 = 0.002 ただし、浮動小数点と double を使用している場合は、12,345,678,900,000,000,000,000+1=12,345,678,900,000,000,000,000 になります。このシステムで 100 万ドル以上を扱うことはないだろうという確信がない限り、これはお金の適切な表現ではありません。

于 2008-12-10T17:24:55.930 に答える
1

セント?お金の値を計算している場合は、実際には浮動小数点値を使用しないでください。お金は実際には数えられる値です。セントやペニーなどは、整数の最下位 2 桁 (または何でも) と見なすことができます。金額の値を整数として保存および計算し、100 で割ることができます (たとえば、最後の 2 桁の前にドットまたはカンマを配置します)。フロートを使用すると、奇妙な丸めエラーが発生する可能性があります...

とにかく、あなたのイプシロンが精度を定義することになっている場合、それは少し小さすぎます(精度が高すぎます)...

于 2008-12-10T17:27:41.600 に答える