在java中操作和比较浮点数

在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位小数的精度感兴趣(#。###)。 我如何处理这个问题来乘以浮点数并可靠地比较它们?

一般来说,浮点数不应该像(a == b)那样进行比较,而是像(Math.abs(ab) < delta) ,其中delta是一个小数字。

具有十进制forms的固定位数的浮点值不必具有二进制forms的固定位数。

为清晰起见添加:

尽管严格==浮点数的比较几乎没有实际意义,但相反,严格的<>比较是一个有效的用例(例如 - 某些值超过阈值时的逻辑触发: (val > threshold) && panic();

如果您对固定精度数字感兴趣,那么您应该使用像BigDecimal这样的固定精度类型,而不是像float这样的固有近似(虽然是高精度)类型。 Stack Overflow上有许多类似的问题,可以在许多语言中更详细地介绍这些问题。

我认为它与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"); } } 

这是所有浮点表示的弱点,并且它发生是因为在十进制系统中看起来具有固定小数位数的某些数字实际上在二进制系统中具有无限小数位数。 所以你认为1.2实际上就像是1.199999999997,因为当用二进制表示它时必须在一定数量之后砍掉小数,你就失去了一些精度。 然后将它乘以3实际上得到3.5999999 ……

http://docs.python.org/py3k/tutorial/floatingpoint.html < - 这可能更好地解释它(即使它是python,它是浮点表示的常见问题)

像其他人写的那样:

将浮点数与: 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 0.001f 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"); } 

有一个用于比较双精度的apache类: org.apache.commons.math3.util.Precision

它包含一些有趣的常量: SAFE_MINEPSILON ,它们是执行算术运算时的最大可能偏差。

它还提供了比较,相等或圆形双打的必要方法。

我在unit testing中使用这段代码来比较2个不同计算的结果是否相同,除非出现浮点数学错误。

它的工作原理是查看浮点数的二进制表示。 大多数并发症是由于浮点数的符号不是两个补码的事实。 在补偿之后,它基本上归结为只是一个简单的减法来获得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. * Eg 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; } 

要比较两个浮点数, f1f2#.###精度范围内#.###我相信你需要这样做:

 ((int) (f1 * 1000 + 0.5)) == ((int) (f2 * 1000 + 0.5)) 

f1 * 1000升降机3.14159265...3141.59265+ 0.5结果在3142.09265 ,而(int)切断小数, 3142 。 也就是说,它包括3位小数并正确舍入最后一位数。