天天记录 - Java 精确计算避免使用float和double


一  问题描述

    float和double类型不能用于精确计算,其主要目的是为了科学计算和工程计算,它们执行二进制浮点原酸,目的是为了广泛的数值范围上提供较为精确的快速近似计算而精心设计的。但是如果设计钱币之类的计算需要很精确,所以这种情况不能使用float和double,因为要让其精确表示0.1 或者 10的任何负数次方值是不可能的。


二 眼见为实,举例证明:

		// float与double 无法精确表示0.1 或者 10的负次方
		System.out.println( 0.15 - 0.05 ); // 0.09999999999999999
		System.out.println( 2.08f - 3.7f );// -1.6200001


三 举一个现实中会遇到的情况:

     一个实际的例子,口袋里面有10元钱,买0.9元的商品十个 , 收银员需要找零1元。计算如下:

10 - 0.9 * 10 = 1
     代码实现:

		float myMoney = 10.00f;
		float price = 0.9f;
		for (int i = 0; i < 10; i++) {
			myMoney -= price;
		}
		System.out.println( myMoney ); // 1.0000002

    结果竟然输出的是1.0000002而非1,可能很多人会认为这也无所谓啊,毕竟仅偏差了0.0000002,但问题是如果前提是进行了1亿笔交易呢?难道你愿意仅仅因为使用了计算机计算这些数据,而损失一部分钱吗?



四 解决办法

1. 使用int 和 long类型,如果小数点以后的值可以忽略不计可以只用这两种类型

2. 对计算结果进行四舍五入

3. 使用BigDecimal类型进行浮点运算


先来看一段代码

		int intValue = 10;
		float floatValue = 0.99999f;
		
		int count1 = (int) (intValue + floatValue);
		System.out.println("10 + 0.99999 = " +  count1); // output 10
		
		int count2 = (int) (intValue - floatValue);
		System.out.println("10 - 0.99999 = " +  count2); // output 9

原本按我个人的理解 10 + 0.99999 = 10.99999 然后转为int类型会进行四舍五入 值为11,而实际结果为10



更深入的分析可以查看


如何解决float和double误差问题呢?可以使用Java中提供的java.math.BigDecimal,不过此类型是不可变的,每次计算都必须新创建一个对象,不适合大量计算


再来看一个舍入误差会出现的问题

                // 10.1 + 0.99*10 = 20
		float f1 = 10.1f;
		float f2 = 0.99f;
		for (int i = 0; i < 10; i++) {
			f1 += f2;
		}
		System.out.println(f1); // 19.999998

解决办法,使用BigDecimal


		BigDecimal count = new BigDecimal("10.1");
		for (int i = 0; i < 10; i++) {
			count = count.add(new BigDecimal("1"));
		}
		
		System.out.println(count.intValue()); // 20





参考资料:

1. Java 理论与实践: 您的小数点到哪里去了?

2. 《Effective Java》 第2版 第48条 如果需要精确的答案,请避免使用float和double



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Javafloatdouble都是用二进制浮点数表示的,它们的精度是有限的。因为二进制浮点数无法精确表示某些十进制数,如0.1,所以在进行精确计算时需要特别注意。 在进行浮点数计算时,可以使用BigDecimal类来实现精确计算。BigDecimal类可以表示任意精度的十进制数,而且支持加、减、乘、除等基本的数学运算。 下面是一个使用BigDecimal类进行浮点数计算的例子: ``` import java.math.BigDecimal; public class FloatDoublePrecision { public static void main(String[] args) { float f1 = 0.1f; float f2 = 0.2f; double d1 = 0.1; double d2 = 0.2; BigDecimal b1 = new BigDecimal(Float.toString(f1)); BigDecimal b2 = new BigDecimal(Float.toString(f2)); BigDecimal b3 = new BigDecimal(Double.toString(d1)); BigDecimal b4 = new BigDecimal(Double.toString(d2)); BigDecimal result1 = b1.add(b2); BigDecimal result2 = b3.add(b4); System.out.println("Float计算结果:" + result1); System.out.println("Double计算结果:" + result2); } } ``` 输出结果如下: ``` Float计算结果:0.300000011920928955078125 Double计算结果:0.3000000000000000444089209850062616169452667236328125 ``` 可以看到,使用floatdouble进行计算得到的结果都存在精度问题。而使用BigDecimal类进行计算可以得到精确的结果。 需要注意的是,使用BigDecimal类进行计算时需要使用字符串形式的构造方法,而不能直接使用浮点数进行构造,否则仍然会存在精度问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值