我写了一个类来测试相等,小于和大于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;
}
答案 0 :(得分:101)
你不要用双倍代表金钱。永远不会。请改用java.math.BigDecimal
。
然后你可以指定如何精确地进行舍入(有时在金融应用程序中由法律规定!)并且不必像这个epsilon那样做愚蠢的黑客。
说真的,使用浮点类型代表金钱是非常不专业的。
答案 1 :(得分:10)
是。 Java双精度将比你给出的0.00001的epsil更好。
由于存储浮点值而发生的任何舍入错误都将小于0.00001。我经常使用1E-6
或0.000001在Java中使用双epsilon,没有任何问题。
在相关的说明中,我喜欢epsilon = 1E-5;
的格式,因为我觉得它更具可读性(Java中的1E-5 = 1 x 10 ^ -5)。读取代码时,1E-6很容易与1E-5区分,而当看到代码时,0.00001和0.000001看起来非常相似,我认为它们是相同的值。
答案 2 :(得分:8)
答案 3 :(得分: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);
}
答案 4 :(得分:5)
如果您正在处理资金,我建议您查看Money设计模式(最初来自Martin Fowler's book on enterprise architectural design)。
我建议阅读此链接以获取动机: http://wiki.moredesignpatterns.com/space/Value+Object+Motivation+v2
答案 5 :(得分:2)
虽然我同意双重对金钱不利的想法,但仍然有兴趣比较双打的想法。特别是epsilon的建议用途仅适用于特定范围内的数字。以下是epsilon的更一般用法,相对于两个数字的比率(省略了0的测试):
boolean equal(double d1, double d2) {
double d = d1 / d2;
return (Math.abs(d - 1.0) < 0.001);
}
答案 6 :(得分:1)
浮点数只有很多有效数字,但它们可以更高。如果您的应用程序将处理大数字,您会注意到epsilon值应该不同。
0.001 + 0.001 = 0.002 但 12,345,678,900,000,000,000,000 + 1 = 12,345,678,900,000,000,000,000 如果你使用浮点和双。这不是一个很好的金钱代表,除非你确定你永远不会在这个系统中处理超过一百万美元。
答案 7 :(得分:1)
仙?如果你计算货币价值,你真的不应该使用浮动值。金钱实际上是可数值。美分或pennys等可以被认为是整数的两个(或任何)最低有效数字。您可以存储并计算货币值作为整数并除以100(例如,在最后两位数之前放置点或逗号两个)。使用float可能导致奇怪的舍入错误......
无论如何,如果你的epsilon应该定义准确度,它看起来有点太小(太准确)......
答案 8 :(得分:0)
正如其他评论者所正确指出的那样,当需要精确值时,永远不会使用浮点运算,例如货币值。主要原因确实是浮点固有的舍入行为,但不要忘记处理浮点意味着还必须处理无限和NaN值。
为了说明您的方法根本不起作用,这里有一些简单的测试代码。我只是将您的EPSILON
添加到10.0
并查看结果是否等于10.0
- 它不应该是,因为差异显然不是 less < / em>而不是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
如果两个浮点值具有不同的指数,则如果减去有效位,则会发生错误。
如果您考虑应用更高级的相对差异&#34;根据其他评论者的建议,你应该阅读Bruce Dawson的优秀文章Comparing Floating Point Numbers, 2012 Edition,这表明这种方法有类似的缺点,实际上没有故障安全近似浮动点比较适用于所有浮点数范围。
简而言之:从货币值double
开始,并使用BigDecimal
之类的确切数字表示。为了提高效率,您还可以使用longs
解释为&#34; millis&#34; (十分之一美分),只要您可靠地防止过量和下溢。这会产生9'223'372'036'854'775.807
的最大可表示值,这对于大多数实际应用程序来说应该足够了。