繁体   English   中英

Java双重比较epsilon

[英]Java double comparison epsilon

我写了一个类来测试相等,小于和大于Java中的两个双重。 我的一般情况是比较可以具有半分精度的价格。 59.005比59.395。 我选择的epsilon适合这些情况吗?

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;
}

你不要用double来代表金钱。 永远不会。 请改用java.math.BigDecimal

然后你可以指定如何精确地进行舍入(有时在金融应用程序中由法律规定!)并且不必像这个epsilon那样做愚蠢的黑客攻击。

说真的,使用浮点类型来代表金钱是非常不专业的。

是。 Java双精度将比你给出的0.00001的epsil更好。

由于存储浮点值而发生的任何舍入误差将小于0.00001。 我经常使用1E-6或0.000001在Java中使用双epsilon,没有任何问题。

在相关的说明中,我喜欢epsilon = 1E-5;的格式epsilon = 1E-5; 因为我觉得它更具可读性(Java中的1E-5 = 1 x 10 ^ -5)。 读取代码时,1E-6很容易与1E-5区分,而当看到代码时,0.00001和0.000001看起来非常相似,我认为它们是相同的值。

哇哇哇哇 您是否有特定的原因使用浮点货币,或者使用任意精度的定点数字格式会更好? 我不知道你要解决的具体问题是什么,但你应该考虑半分钱是否真的是你想要使用的东西,或者它只是一个使用不精确数字格式的工件。

如果你可以使用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);
}

如果你正在处理钱,我建议检查Money设计模式(最初来自Martin Fowler关于企业架构设计的书 )。

我建议阅读此链接以获取动机: http//wiki.moredesignpatterns.com/space/Value+Object+Motivation+v2

虽然我同意双重对金钱不利的想法,但仍然有利于比较双打的想法。 特别是epsilon的建议用途仅适用于特定范围内的数字。 以下是epsilon的更一般用法,相对于两个数字的比率(省略了0的测试):

boolean equal(double d1, double d2) {
  double d = d1 / d2;
  return (Math.abs(d - 1.0) < 0.001);
}

浮点数只有很多有效数字,但它们可以更高。 如果您的应用程序将处理大数字,您会注意到epsilon值应该不同。

0.001 + 0.001 = 0.002但如果您使用浮点和双倍,则为12,345,678,900,000,000,000,000,000 + 1 = 12,345,678,900,000,000,000,000,000。 这不是一个很好的金钱代表,除非你确定你永远不会在这个系统中处理超过一百万美元。

仙? 如果你计算货币价值,你真的不应该使用浮动值。 金钱实际上是可数值。 美分或pennys等可以被认为是整数的两个(或任何)最低有效数字。 您可以存储,并将货币值计算为整数并除以100(例如,在最后两位数之前放置点或逗号两位)。 使用float可能会导致奇怪的舍入错误......

无论如何,如果你的epsilon应该定义准确度,它看起来有点太小(太准确)......

正如其他评论者正确地指出,当需要精确值,如货币值,你应该使用浮点运算。 主要原因确实是浮点内在的舍入行为,但是不要忘记处理浮点意味着还必须处理无限和NaN值。

举例来说,您的方法根本不起作用,这里有一些简单的测试代码。 我只是将您的EPSILON添加到10.0并查看结果是否等于10.0 - 它不应该是,因为差异显然不小于 EPSILON

    double a = 10.0;
    double b = 10.0 + EPSILON;
    if (!equals(a, b)) {
        System.out.println("OK: " + a + " != " + b);
    } else {
        System.out.println("ERROR: " + a + " == " + b);
    }

惊喜:

    ERROR: 10.0 == 10.00001

如果两个浮点值具有不同的指数,则如果减去有效位则丢失,则会发生错误。

如果您考虑应用其他评论者建议的更高级的“相对差异”方法,您应该阅读Bruce Dawson的优秀文章“ 比较浮点数”,2012年版 ,这表明这种方法有类似的缺点,并且实际上没有失败 -安全的近似浮点比较,适用于所有浮点数范围。

简而言之:取消货币价值的double ,并使用确切的数字表示,如BigDecimal 为了提高效率起见,你也可以使用longs解释为“米利斯”(美分的十分之一),只要你可靠地防止过压和下溢。 这产生了最大可表示的值9'223'372'036'854'775.807 ,这对于大多数现实世界的应用应该是足够的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM