如何在Java中使用浮点数或浮点数来避免浮点精度错误?
我有一个非常烦人的问题,在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.1没有一个float
或double
float
expression式。 由于这种表示错误,结果与您的预期略有不同。
你可以使用几种方法:
- 使用
double
型时,只显示所需数量的数字。 当检查平等允许任何方式的小容忍。 - 或者,使用允许您存储准确表示的数字的types,例如
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
你可以使用像BigDecimal
这样的类来避免这个特定的问题。 float
和double
float
,IEEE 754浮点,不是精确的devise,它们devise得很快。 但请注意Jon的要点: BigDecimal
不能准确地表示“三分之一”,任何超过二分之一的准确表示“十分之一”。 但是对于(比方说)财务计算, BigDecimal
和类似的类往往是要走的路,因为它们可以用我们人类倾向于考虑的方式来表示数字。
不要在迭代器中使用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是一个stream行的select,但我更喜欢双倍,不是因为它更快,但它通常要更短或更清晰的理解。
如果您计算符号的数量作为代码复杂性的度量
- 使用double => 11个符号
- 使用BigDecimal(来自@Mark Byers示例)=> 21个符号
顺便说一句:不要使用浮动,除非有一个很好的理由不使用双。
这不仅仅是一个累积的错误(与Java完全没有关系)。 1.0f
,一旦翻译成实际的代码,不具有0.1的值 – 你已经得到一个四舍五入的错误。
从浮点指南:
我能做些什么来避免这个问题?
这取决于你在做什么样的计算。
- 如果你真的需要你的结果加起来,特别是当你使用金钱:使用一个特殊的十进制数据types。
- 如果您不想看到所有这些额外的小数位:只需在显示时将结果格式化为固定的小数位数。
- 如果你没有可用的小数数据types,另一种方法是使用整数,例如完全以美分计算。 但这是更多的工作,并有一些缺点。
阅读链接到的网站获取详细信息。
为了完整起见,我推荐这个:
Shewchuck,“稳健的自适应浮点几何谓词”,如果你想要更多的例子来说明如何进行精确的浮点计算 – 或者至less是作者的初衷是控制精度。埃杜/〜JRS /纸/ robustr.pdf
另一个解决scheme是放弃==
并检查两个值是否足够接近 。 (我知道这不是你所要求的,但是我正在回答问题的标题。)
我遇到了同样的问题,使用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);
希望它会帮助你。
你应该使用十进制数据types,而不是浮动:
https://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html
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)); } }
如果你想继续使用float
并避免通过反复添加0.1f
累积错误,请尝试如下所示:
for (int count = 0; count < 10; count++) { float value = 0.1f * count; System.out.println(value); }
但是请注意,正如其他人已经解释的那样, float
不是一个无限精确的数据types。
您只需要知道您的计算所需的精度以及所选数据types的精度,并相应地显示您的答案。
例如,如果您处理的是具有3位有效数字的数字,则使用float
(提供7位有效数字的精度)是适当的。 但是,如果您的起始值只有2位有效数字的精度,则无法引用您的最终答案,即7位有效数字的精度。
5.01 + 4.02 = 9.03 (to 3 significant figures)
在你的例子中,你正在执行多个添加,每增加一个结果就会对最终的精度产生影响。