在Java中,没有精确表示浮点运算。例如这个java代码:
float a = 1.2;
float b= 3.0;
float c = a * b;
if(c == 3.6){
System.out.println("c is 3.6");
}
else {
System.out.println("c is not 3.6");
}
打印“c不是3.6”。
我对超过3位小数的精度感兴趣(#。###)。我如何处理这个问题来乘以浮点数并可靠地比较它们?
答案 0 :(得分:18)
一般来说,浮点数不应该像(a == b)那样进行比较,而是像(Math.abs(a-b) < delta)
那样 delta 是一个小的号。
具有十进制形式的固定位数的浮点值不一定具有二进制形式的固定位数。
为了清楚起见,增加了:
虽然严格==
浮点数的比较几乎没有实际意义,但严格的<
和>
比较恰恰相反,是一个有效的用例(例如 - 逻辑触发)当某个值超过阈值时:(val > threshold) && panic();
)
答案 1 :(得分:7)
如果您对固定精度数字感兴趣,则应该使用类似BigDecimal
的固定精度类型,而不是像float
这样的固有近似(虽然是高精度)类型。 Stack Overflow上有许多类似的问题,可以通过多种语言更详细地介绍这些问题。
答案 2 :(得分:4)
我认为它与Java无关,它发生在任何IEEE 754浮点数上。这是因为浮点表示的本质。任何使用IEEE 754格式的语言都会遇到同样的问题。
正如David上面所建议的那样,你应该使用java.lang.Math类的方法abs来获取绝对值(放下正/负号)。
您可以阅读:http://en.wikipedia.org/wiki/IEEE_754_revision并且一本好的数值方法教科书将充分解决这个问题。
public static void main(String[] args) {
float a = 1.2f;
float b = 3.0f;
float c = a * b;
final float PRECISION_LEVEL = 0.001f;
if(Math.abs(c - 3.6f) < PRECISION_LEVEL) {
System.out.println("c is 3.6");
} else {
System.out.println("c is not 3.6");
}
}
答案 3 :(得分:3)
我在单元测试中使用这段代码来比较两个不同计算的结果是否相同,除非出现浮点数学错误。
通过查看浮点数的二进制表示来工作。大多数并发症是由于浮点数的符号不是二进制的补码。在补偿之后,它基本上只是简单的减法来获得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;
}
答案 4 :(得分:2)
这是所有浮点表示的弱点,并且它发生是因为在十进制系统中看起来具有固定小数位数的某些数字实际上在二进制系统中具有无限小数位数。所以你认为1.2实际上就像是1.199999999997,因为当用二进制表示它时必须在一定数量之后砍掉小数,你就会失去一些精度。然后乘以3实际上得到3.5999999 ...
http://docs.python.org/py3k/tutorial/floatingpoint.html&lt; - 这可能更好地解释它(即使它是python,它是浮点表示的常见问题)
答案 5 :(得分:2)
像其他人一样写道:
将浮点数与
进行比较if (Math.abs(a - b) < delta)
你可以写一个很好的方法:
public static int compareFloats(float f1, float f2, float delta)
{
if (Math.abs(f1 - f2) < delta)
{
return 0;
} else
{
if (f1 < f2)
{
return -1;
} else {
return 1;
}
}
}
/**
* Uses <code>0.001f</code> for delta.
*/
public static int compareFloats(float f1, float f2)
{
return compareFloats(f1, f2, 0.001f);
}
所以,你可以像这样使用它:
if (compareFloats(a * b, 3.6f) == 0)
{
System.out.println("They are equal");
}
else
{
System.out.println("They aren't equal");
}
答案 6 :(得分:2)
有一个用于比较双打的apache类:org.apache.commons.math3.util.Precision
它包含一些有趣的常量:SAFE_MIN
和EPSILON
,它们是执行算术运算时可能出现的最大偏差。
它还提供了比较,相等或圆形双打的必要方法。
答案 7 :(得分:0)
舍入是一个坏主意。使用BigDecimal
并根据需要设置其精度。
喜欢:
public static void main(String... args) {
float a = 1.2f;
float b = 3.0f;
float c = a * b;
BigDecimal a2 = BigDecimal.valueOf(a);
BigDecimal b2 = BigDecimal.valueOf(b);
BigDecimal c2 = a2.multiply(b2);
BigDecimal a3 = a2.setScale(2, RoundingMode.HALF_UP);
BigDecimal b3 = b2.setScale(2, RoundingMode.HALF_UP);
BigDecimal c3 = a3.multiply(b3);
BigDecimal c4 = a3.multiply(b3).setScale(2, RoundingMode.HALF_UP);
System.out.println(c); // 3.6000001
System.out.println(c2); // 3.60000014305114740
System.out.println(c3); // 3.6000
System.out.println(c == 3.6f); // false
System.out.println(Float.compare(c, 3.6f) == 0); // false
System.out.println(c2.compareTo(BigDecimal.valueOf(3.6f)) == 0); // false
System.out.println(c3.compareTo(BigDecimal.valueOf(3.6f)) == 0); // false
System.out.println(c3.compareTo(BigDecimal.valueOf(3.6f).setScale(2, RoundingMode.HALF_UP)) == 0); // true
System.out.println(c3.compareTo(BigDecimal.valueOf(3.6f).setScale(9, RoundingMode.HALF_UP)) == 0); // false
System.out.println(c4.compareTo(BigDecimal.valueOf(3.6f).setScale(2, RoundingMode.HALF_UP)) == 0); // true
}
答案 8 :(得分:-1)
要比较两个浮点数f1
和f2
,精度为#.###
我相信您需要这样做:
((int) (f1 * 1000 + 0.5)) == ((int) (f2 * 1000 + 0.5))
f1 * 1000
将3.14159265...
提升为3141.59265
,+ 0.5
会产生3142.09265
,(int)
剁掉小数,3142
。也就是说,它包括3位小数并正确地舍入最后一位数。