在java中操作和比较浮点数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2896013/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Manipulating and comparing floating points in java
提问by Praneeth
In Java the floating point arithmetic is not represented precisely. For example this java code:
在 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");
}
Prints "c is not 3.6".
打印“c 不是 3.6”。
I'm not interested in precision beyond 3 decimals (#.###). How can I deal with this problem to multiply floats and compare them reliably?
我对超过 3 位小数 (#.###) 的精度不感兴趣。我该如何处理这个问题来乘以浮点数并可靠地比较它们?
回答by David M
If you are interested in fixed precision numbers, you should be using a fixed precision type like BigDecimal, not an inherently approximate (though high precision) type like float. There are numerous similar questions on Stack Overflow that go into this in more detail, across many languages.
如果您对固定精度数感兴趣,您应该使用固定精度类型,例如BigDecimal,而不是像float. Stack Overflow 上有许多类似的问题,这些问题更详细地涉及多种语言。
回答by bobah
It's a general rule that floating point number should never be compared like (a==b), but rather like (Math.abs(a-b) < delta)where deltais a small number.
这是一个一般的规则,即浮点数应该永远无法比拟的像(A == B) ,而是喜欢(Math.abs(a-b) < delta)在那里三角洲是一个小数目。
A floating point value having fixed number of digits in decimal form does not necessary have fixed number of digits in binary form.
十进制形式的具有固定位数的浮点值不一定具有二进制形式的固定位数。
Addition for clarity:
为清楚起见补充:
Though strict ==comparison of floating point numbers has very little practical sense, the strict <and >comparison, on the contrary, is a valid use case (example - logic triggering when certain value exceeds threshold: (val > threshold) && panic();)
虽然严格的==浮点数的比较具有非常小的实际意义上说,严格的<和>比较,相反,是一个有效的用例(实施例-当某些值超过阈值的逻辑触发:(val > threshold) && panic();)
回答by Daniel Baktiar
I think it has nothing to do with Java, it happens on any IEEE 754 floating point number. It is because of the nature of floating point representation. Any languages that use the IEEE 754 format will encounter the same problem.
我认为它与 Java 无关,它发生在任何 IEEE 754 浮点数上。这是因为浮点表示的性质。任何使用 IEEE 754 格式的语言都会遇到同样的问题。
As suggested by David above, you should use the method abs of java.lang.Math class to get the absolute value (drop the positive/negative sign).
正如上面 David 所建议的,您应该使用 java.lang.Math 类的方法 abs 来获取绝对值(去掉正/负号)。
You can read this: http://en.wikipedia.org/wiki/IEEE_754_revisionand also a good numerical methods text book will address the problem sufficiently.
您可以阅读: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");
}
}
回答by aioobe
To compare two floats, f1and f2within precision of #.###I believe you would need to do like this:
要比较两个浮点数,f1并且f2在#.###我相信的精度范围内,您需要这样做:
((int) (f1 * 1000 + 0.5)) == ((int) (f2 * 1000 + 0.5))
f1 * 1000lifts 3.14159265...to 3141.59265, + 0.5results in 3142.09265and the (int)chops off the decimals, 3142. That is, it includes 3 decimals and rounds the last digit properly.
f1 * 1000提升3.14159265...到3141.59265,+ 0.5结果3142.09265并(int)去掉小数点, 3142。也就是说,它包括 3 位小数并正确舍入最后一位。
回答by Andrei Fierbinteanu
This is a weakness of all floating point representations, and it happens because some numbers that appear to have a fixed number of decimals in the decimal system, actually have an infinite number of decimals in the binary system. And so what you think is 1.2 is actually something like 1.199999999997 because when representing it in binary it has to chop off the decimals after a certain number, and you lose some precision. Then multiplying it by 3 actually gives 3.5999999...
这是所有浮点表示的一个弱点,这是因为一些在十进制系统中看起来有固定小数位数的数字,实际上在二进制系统中有无限个小数位。所以你认为 1.2 实际上类似于 1.199999999997 因为当用二进制表示它时,它必须在某个数字后去掉小数,你会失去一些精度。然后将其乘以 3 实际上得到 3.5999999 ...
http://docs.python.org/py3k/tutorial/floatingpoint.html<- this might explain it better (even if it's for python, it's a common problem of the floating point representation)
http://docs.python.org/py3k/tutorial/floatingpoint.html<- 这可能会更好地解释它(即使它是用于 python,这是浮点表示的一个常见问题)
回答by Martijn Courteaux
Like the others wrote:
就像其他人写道:
Compare floats with:
if (Math.abs(a - b) < delta)
比较浮点数:
if (Math.abs(a - b) < delta)
You can write a nice method for doing this:
您可以编写一个很好的方法来执行此操作:
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);
}
So, you can use it like this:
所以,你可以像这样使用它:
if (compareFloats(a * b, 3.6f) == 0)
{
System.out.println("They are equal");
}
else
{
System.out.println("They aren't equal");
}
回答by Laurens Holst
I'm using this bit of code in unit tests to compare if the outcome of 2 different calculations are the same, barring floating point math errors.
我在单元测试中使用这段代码来比较 2 个不同计算的结果是否相同,除非出现浮点数学错误。
It works by looking at the binary representation of the floating point number. Most of the complication is due to the fact that the sign of floating point numbers is not two's complement. After compensating for that it basically comes down to just a simple subtraction to get the difference in ULPs (explained in the comment below).
它通过查看浮点数的二进制表示来工作。大多数复杂情况是由于浮点数的符号不是二进制补码这一事实。在补偿之后,它基本上归结为一个简单的减法来获得 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;
}
Here is a version for doubleprecision floats:
这是一个用于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;
}
回答by bvdb
There is an apache class for comparing doubles: org.apache.commons.math3.util.Precision
有一个用于比较双打的 apache 类:org.apache.commons.math3.util.Precision
It contains some interesting constants: SAFE_MINand EPSILON, which are the maximum possible deviations when performing arithmetic operations.
它包含一些有趣的常量:SAFE_MIN和EPSILON,它们是执行算术运算时的最大可能偏差。
It also provides the necessary methods to compare, equal or round doubles.
它还提供了必要的方法来比较、相等或舍入双打。
回答by Aldis
Rounding is a bad idea. Use BigDecimaland set it's precision as needed.
Like:
舍入是一个坏主意。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
}

