[英]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.