我有一个非常恼人的问题,在Java中使用了很多浮点数或双精度数。基本上我的想法是,如果我执行:
for ( float value = 0.0f; value < 1.0f; value += 0.1f )
System.out.println( value );
我得到的是:
0.0
0.1
0.2
0.3
0.4
0.5
0.6
0.70000005
0.8000001
0.9000001
我明白浮动精度误差的累积,但是,如何摆脱这个?我尝试使用双打错误的一半,但结果仍然相同。
有什么想法吗?
答案 0 :(得分:33)
float
或double
没有精确的0.1表示。由于这种表示错误,结果与您的预期略有不同。
您可以使用以下几种方法:
double
类型时,只显示所需数量的数字。检查相等性时,无论如何都允许小容差。BigDecimal
可以精确地表示0.1。 BigDecimal
的示例代码:
BigDecimal step = new BigDecimal("0.1");
for (BigDecimal value = BigDecimal.ZERO;
value.compareTo(BigDecimal.ONE) < 0;
value = value.add(step)) {
System.out.println(value);
}
在线查看:ideone
答案 1 :(得分:9)
您可以使用BigDecimal
等类来避免此特定问题。作为IEEE 754浮点的float
和double
并非完全准确,它们的设计速度很快。但请注意Jon的观点:BigDecimal
不能准确地表示“三分之一”,超过double
可以准确地表示“十分之一”。但是对于(比如说)财务计算,BigDecimal
和类似的课程往往是要走的路,因为它们可以用我们人类倾向于思考它们的方式来表示数字。
答案 2 :(得分:7)
不要在迭代器中使用float / double,因为这会使舍入误差最大化。如果您只使用以下
for (int i = 0; i < 10; i++)
System.out.println(i / 10.0);
打印
0.0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9
我知道BigDecimal是一个受欢迎的选择,但我更喜欢双倍,因为它的速度要快得多,但通常更短/更清晰。
如果计算符号数量作为代码复杂度的度量
BTW:除非确实有充分的理由不使用double,否则不要使用float。
答案 3 :(得分:3)
它不只是一个累积的错误(并且与Java完全无关)。 1.0f
,一旦转换为实际代码,则没有值0.1 - 您已经收到舍入错误。
我该怎么做才能避免这个问题?
这取决于什么样的 你正在做的计算。
- 如果你真的需要你的结果准确加起来,特别是当你使用钱时:使用特殊的十进制数据类型。
- 如果您只是不想看到所有这些额外的小数位:只需将结果格式化为固定的 小数位数 显示它。
- 如果您没有可用的十进制数据类型,则可以使用另一种方法 用整数,例如做钱 完全以美分计算。但 这是更多的工作,并有一些 缺点。
阅读链接到站点以获取详细信息。
答案 4 :(得分:3)
为了完整起见,我推荐这个:
Shewchuck,&#34;鲁棒自适应浮点几何谓词&#34;,如果你想要更多关于如何用浮点执行精确算术的例子 - 或者至少是控制精度,这是作者的初衷,{{ 3}}
答案 5 :(得分:2)
您应该使用十进制数据类型,而不是浮点数:
https://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html
答案 6 :(得分:2)
另一个解决方案是放弃==
并检查两个值是否足够接近。 (我知道这不是你在身体里问的,但我正在回答问题标题。)
答案 7 :(得分:2)
我遇到过同样的问题,使用BigDecimal解决了同样的问题。以下是帮助我的片段。
double[] array = {45.34d, 45000.24d, 15000.12d, 4534.89d, 3444.12d, 12000.00d, 4900.00d, 1800.01d};
double total = 0.00d;
BigDecimal bTotal = new BigDecimal(0.0+"");
for(int i = 0;i < array.length; i++) {
total += (double)array[i];
bTotal = bTotal.add(new BigDecimal(array[i] +""));
}
System.out.println(total);
System.out.println(bTotal);
希望它会对你有所帮助。
答案 8 :(得分:2)
package loopinamdar;
import java.text.DecimalFormat;
public class loopinam {
static DecimalFormat valueFormat = new DecimalFormat("0.0");
public static void main(String[] args) {
for (float value = 0.0f; value < 1.0f; value += 0.1f)
System.out.println("" + valueFormat.format(value));
}
}
答案 9 :(得分:1)
首先将其设为 double 。不要使用 float ,否则您将无法使用java.lang.Math
实用程序。
现在,如果您事先知道所需的精度且等于或小于15,那么很容易告诉您的 double 行为。检查以下内容:
// the magic method:
public final static double makePrecise(double value, int precision) {
double pow = Math.pow(10, precision);
long powValue = Math.round(pow * value);
return powValue / pow;
}
现在无论何时进行操作,都必须告诉 double 结果:
for ( double value = 0.0d; value < 1.0d; value += 0.1d )
System.out.println( makePrecise(value, 1) + " => " + value );
输出:
0.0 => 0.0
0.1 => 0.1
0.2 => 0.2
0.3 => 0.30000000000000004
0.4 => 0.4
0.5 => 0.5
0.6 => 0.6
0.7 => 0.7
0.8 => 0.7999999999999999
0.9 => 0.8999999999999999
1.0 => 0.9999999999999999
如果你需要超过15的精度,那么你就不走运了:
for ( double value = 0.0d; value < 1.0d; value += 0.1d )
System.out.println( makePrecise(value, 16) + " => " + value );
输出:
0.0 => 0.0
0.1 => 0.1
0.2 => 0.2
0.3000000000000001 => 0.30000000000000004
0.4 => 0.4
0.5 => 0.5
0.6 => 0.6
0.7 => 0.7
0.8 => 0.7999999999999999
0.9 => 0.8999999999999999
0.9999999999999998 => 0.9999999999999999
NOTE1:为了提高性能,您应该将Math.pow
操作缓存在一个数组中。为清楚起见,这里没有做到。
注意2:这就是为什么我们永远不会将双用于价格,而 long 的最后N位置(即N <= 15,通常为8)数字是十进制数字。然后你可以忘记我上面写的内容:))
答案 10 :(得分:0)
如果您想继续使用float
并避免通过重复添加0.1f
来累积错误,请尝试以下操作:
for (int count = 0; count < 10; count++) {
float value = 0.1f * count;
System.out.println(value);
}
但请注意,正如其他人已经解释的那样,float
不是一种无限精确的数据类型。
答案 11 :(得分:0)
您只需要了解计算所需的精度以及所选数据类型的精确度,并相应地提供答案。
例如,如果您正在处理具有3个有效数字的数字,则使用float
(提供7个有效数字的精度)是合适的。但是,如果您的起始值仅具有2位有效数字的精度,则无法将最终答案引用为7位有效数字的精确度。
5.01 + 4.02 = 9.03 (to 3 significant figures)
在您的示例中,您正在执行多次添加,并且每次添加都会对最终精度产生影响。