由BigDecimal引发的思考

Java浮点运算与BigDecimal
本文探讨了Java中float和double类型的运算不准确性,并通过示例介绍了如何利用BigDecimal类进行精确计算,包括构造方法的选择及常见操作。

本文综合网上牛人以及Java API所写,还有自己的一些项目经历。主要实用工具Eclipse3.5、jdk1.6,测试框架JUnit4等。

floatdouble只能用来做科学计算或者是工程计算,在商业计算中我们要用java.math.BigDecimal

                                                                                                                                       -----《Effective Java

看一个小例子:

 

测试结果:

 

让人不可思议,怎么会与自己的想象差的那么远!但,现实确实如此!

Java中的简单浮点数类型floatdouble不能够进行运算。不光是Java,在其它很多编程语言中也有这样的问题。在大多数情况下,计算的结果是准确的,但是多试几次(可以做一个循环)就可以试出类似上面的错误。

再者,Math类、DecimalFormat类有对应的处理数据的方法。实例代码:

 

显示结果:

 

哎,还是让人很失望。

难道在工程中,我们就这样计算吗?当然不是,Java还没有“挫”,呵呵!下面谈谈BigDecimal。

BigDecimal有很多构造方法,详看api文档。这里主要说说下面两个构造方法:

 

关于其他的构造方法,这里不赘述,举一反三吧!

这两个构造方法,建议采用第二个,具体原因在api上解释的很清楚。

 

翻译一下:

 

使用BigDecimal,可以获得交准确的计算结果,并且可以自己定义小数精度,具有一定的灵活性。实例代码:

 

其中,BigDecimal有很多常量字段。ROUND_HALF_UP表示四舍五入。实例代码:

 

在JUnit4的帮助下,很好地完成了许多测试,解开了很多疑惑。如果你不明白最好还是多加测试。也许,你有更多的发现。

在好奇心的驱使下,又看看BigDecimal源码,主要涉及到其valueOf(double val)方法,该方法是静态方法,源码如下:

 

可以看出,valueOf(double val)方法以BigDecimal(Double.toString(val))方式返回一个BigDecimal对象。所以在以double为源的时候创

建BigDecimal对象,可以采用该方法。修改上面实例代码如下:

 

在看源代码的时候,经常看到别人使用valueof**方法,难道它有什么与众不同吗?研究一下ing.....

首先,研究一下Integer的创建方式:

 

根据注释来看,equals比较之后都是true,这很好理解。因为Integer重写了equals方法,只要内容相同,equals比较就是true。再者new方式产出的对象用”=“比较肯定是false,但是为什么同样是valueOf,一个是true,而另一个却是false???!!!

不要急,看看Integer源码:

 

IntegerCache是静态内部类。从源码可以看出使用valueOf方法创建Integer实例时:

当这个值在-128和127之间时,会用缓存保存起来,供多次使用,以节约内存。 
如果不在这个范围内,则创建一个新的Integer对象。 再看一个实例:

 

晕。。居然false,why????

反编译一下这段代码,看看它神秘的面纱。

 

哦,原来如此。自动装箱使用的就是valueOf方法,额的神啊!那么自动拆箱是什么原理?

 

另外,算是提示吧?!

初始化一个字符串,  String s1 = "s1"; 这样的代码肯定比  String s2 = new String("s2");代码强,将其他类型的值转换成String的时候,valueof方法比new方法效率也高。 

文章到此为止,希望对你有用!


非常感谢:

http://www.cnblogs.com/sharewind/archive/2007/09/08/886749.html 提供大量关于BigDecimal的例子代码

http://honda418.iteye.com/blog/315893 探讨了Integer的陷阱 

http://www.cnblogs.com/danne823/archive/2011/04/22/2025332.html 为自动装箱与拆箱提供精彩的代码

 

这篇文章引用了他们的某些代码片段,如果不是他们的无私奉献,估计我没有勇气和能力写出这篇文章,再次感谢他们,向他们学习!!! 

int codeLevel = organizationService.getComlevel(agriculturalInsuranceRiskData.getComCode()); agriculturalInsuranceRiskData.setComLevel(codeLevel); Map<String, Object> map = new HashMap<>(); PageMethod.startPage(agriculturalInsuranceRiskData.getPage(), agriculturalInsuranceRiskData.getRows()); if (StringUtils.equals(CASE_DIMENSION_BREEDING_INSURANCE, agriculturalInsuranceRiskData.getCaseDimension())) { List<AgriculturalBreedingInsuranceRiskData> maps = agriculturalInsuranceRiskDataMapper.queryAgriculturalBreedingInsuranceRiskData(agriculturalInsuranceRiskData); PageInfo<AgriculturalBreedingInsuranceRiskData> pageInfo = new PageInfo<>(maps); map.put("rows", pageInfo.getList()); map.put("pageCount", pageInfo.getPages()); map.put("pageSize", pageInfo.getPageSize()); map.put("total", pageInfo.getTotal()); } else if (StringUtils.equals(CASE_DIMENSION_PLANTING_INSURANCE, agriculturalInsuranceRiskData.getCaseDimension())) { List<AgriculturalPlantingInsuranceRiskData> maps = agriculturalInsuranceRiskDataMapper.queryAgriculturalPlantingInsuranceRiskData(agriculturalInsuranceRiskData); PageInfo<AgriculturalPlantingInsuranceRiskData> pageInfo = new PageInfo<>(maps); map.put("rows", pageInfo.getList()); map.put("pageCount", pageInfo.getPages()); map.put("pageSize", pageInfo.getPageSize()); map.put("total", pageInfo.getTotal()); } else if (StringUtils.equals(CASE_DIMENSION_INNOVATIVE_PRODUCTS, agriculturalInsuranceRiskData.getCaseDimension())) { List<AgriculturalInnovativeProductsRiskData> maps = agriculturalInsuranceRiskDataMapper.queryAgriculturalInnovativeProductsRiskData(agriculturalInsuranceRiskData); PageInfo<AgriculturalInnovativeProductsRiskData> pageInfo = new PageInfo<>(maps); map.put("rows", pageInfo.getList()); map.put("pageCount", pageInfo.getPages()); map.put("pageSize", pageInfo.getPageSize()); map.put("total", pageInfo.getTotal()); } map.put("pageNo", agriculturalInsuranceRiskData.getPage()); return map; 这段代码优化
06-09
### BigDecimal 的用法及常见问题解析 在 Java 中,`BigDecimal` 是一个用于高精度计算的类,尤其适用于金融、科学计算和统计分析等需要精确值处理的场景[^1]。以下是 `BigDecimal` 的基本用法和常见问题的详细解析。 #### 一、BigDecimal 的基本用法 `BigDecimal` 提供了多种构造方法来创建对象,例如通过字符串或字创建。以下是一些常用的构造方法: ```java BigDecimal b1 = new BigDecimal("1"); // 推荐使用字符串形式避免浮点误差 BigDecimal b2 = new BigDecimal(2); // 使用整构造 ``` ##### 常见操作方法 `BigDecimal` 提供了丰富的算术运算方法,包括加减乘除等操作: - **加法**:`add(BigDecimal augend)` - **减法**:`subtract(BigDecimal subtrahend)` - **乘法**:`multiply(BigDecimal multiplicand)` - **除法**:`divide(BigDecimal divisor)` 或 `divide(BigDecimal divisor, int scale, int roundingMode)` 示例代码如下: ```java import java.math.BigDecimal; public class Test { public static void main(String[] args) { BigDecimal b1 = new BigDecimal("1"); BigDecimal b2 = new BigDecimal("2"); BigDecimal b3 = new BigDecimal("4"); System.out.println("相加:" + b1.add(b2)); // 输出 3 System.out.println("相减:" + b1.subtract(b2)); // 输出 -1 System.out.println("相乘:" + b2.multiply(b3)); // 输出 8 System.out.println("相除:" + b2.divide(b3, 2, BigDecimal.ROUND_HALF_UP)); // 输出 0.50 } } ``` #### 二、BigDecimal 的舍入模式 `BigDecimal` 在进行除法运算时,可能无法得到精确结果,因此需要指定保留的小和舍入模式。常见的舍入模式包括: - `BigDecimal.ROUND_DOWN`:直接截断多余部分[^4]。 - `BigDecimal.ROUND_HALF_UP`:四舍五入[^4]。 示例代码如下: ```java import java.math.BigDecimal; public class RoundingExample { public static void main(String[] args) { BigDecimal a = new BigDecimal("0.098").setScale(2, BigDecimal.ROUND_DOWN); // 0.09 BigDecimal b = new BigDecimal("0.094").setScale(2, BigDecimal.ROUND_DOWN); // 0.09 BigDecimal c = new BigDecimal("-0.098").setScale(2, BigDecimal.ROUND_DOWN); // -0.09 BigDecimal d = new BigDecimal("-0.094").setScale(2, BigDecimal.ROUND_DOWN); // -0.09 System.out.println(a); // 输出 0.09 System.out.println(b); // 输出 0.09 System.out.println(c); // 输出 -0.09 System.out.println(d); // 输出 -0.09 } } ``` #### 三、常见问题及解决方法 1. **空指针异常** 在使用 `BigDecimal` 进行运算时,必须确保对象本身不为 `null`,否则会抛出空指针异常[^2]。 2. **据截断问题** 在项目运行过程中,可能会遇到 `java.sql.DataTruncation` 异常,通常是因为据库字段长度不足导致据被截断[^3]。可以通过调整据库字段长度或设置合适的舍入模式来解决。 3. **除法无限循环小** 当除法结果为无限循环小时,`BigDecimal` 默认会抛出异常。可以通过指定保留的小和舍入模式来避免该问题[^3]。 示例代码如下: ```java import java.math.BigDecimal; public class DivisionExample { public static void main(String[] args) { BigDecimal num1 = new BigDecimal("1"); BigDecimal num2 = new BigDecimal("3"); try { BigDecimal result = num1.divide(num2, 10, BigDecimal.ROUND_HALF_UP); // 保留 10 位小,四舍五入 System.out.println("除法结果:" + result); // 输出 0.3333333333 } catch (ArithmeticException e) { System.out.println("发生异常:" + e.getMessage()); } } } ``` #### 四、注意事项 - **构造方式**:推荐使用字符串构造 `BigDecimal` 对象,以避免浮点精度问题[^1]。 - **线程安全性**:`BigDecimal` 是不可变类,因此是线程安全的。 - **性能问题**:由于 `BigDecimal` 的高精度特性,其性能较普通浮点运算稍差,应根据实际需求权衡使用。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值