java中的float、double计算存在精度问题,这不仅仅在java会出现,在其他语言中也会存在,其原因是出在IEEE 754标准上。

而java对此提供了一个用于浮点型计算的类——BigDecimal(java.math.BigDecimal),通过将double替换成BigDecimal进行计算可以获得较为精确的计算结果。

BigDecimal的构造方法有许多,在此推荐使用BigDecimal(String val)的构造方法,通过String字符串进行构造。可能会有人直接使用BigDecimal(double val)去构造,但为什么推荐要使用String而不用double直接构造?原因如BigDecimal(double val)前的注释所说:

Translates a {@code double} into a {@code BigDecimal} which is the exact decimal representation of the {@code double}'s binary floating-point value. The scale of the returned {@code BigDecimal} is the smallest value such that (10 scale val) is an integer.
    Notes: The results of this constructor can be somewhat unpredictable. One might assume that writing {@code new BigDecimal(0.1)} in Java creates a {@code BigDecimal} which is exactly equal to 0.1 (an unscaled value of 1, with a scale of 1), but it is actually equal to 0.1000000000000000055511151231257827021181583404541015625.This is because 0.1 cannot be represented exactly as a {@code double} (or, for that matter, as a binary fraction of any finite length). Thus, the value that is being passed in to the constructor is not exactly equal to 0.1, appearances notwithstanding.
    The {@code String} constructor, on the other hand, is perfectly predictable: writing {@code new BigDecimal("0.1")} creates a {@code BigDecimal} which is exactly equal to 0.1, as one would expect. Therefore, it is generally recommended that the {@linkplain #BigDecimal(String) String constructor} be used in preference to this one.
    When a {@code double} must be used as a source for a {@code BigDecimal}, note that this constructor provides an exact conversion; it does not give the same result as converting the {@code double} to a {@code String} using the {@link Double#toString(double)} method and then using the {@link #BigDecimal(String)} constructor. To get that result, use the {@code static} {@link #valueOf(double)} method.

balabala的一大段?挑出重点看,Notes(说明):The results of this constructor can be somewhat unpredictable(这个结果可能是不可预测的)。原因在后面一句话也说了,若用 0.1 new一个BigDecimal,则它实际上是等于0.1000000000000000055511151231257827021181583404541015625,原因无需细看,跳过。

The {@code String} constructor, on the other hand, is perfectly predictable(通过字符串构造,是可以完全预测的)。到此注释说的差不多了。

而BigDecimal中一系列的add、subtract等方法对应着加减乘除就不必多说。

到这里就结束了吗?不,如果单纯的将double替换成BigDecimal,就会大幅降低程序的运行速度,因此需要进行一定的优化:非替换,而是改进。

依旧用double定义、储存数据,但计算时,使用BigDecimal进行计算(若需要精确的计算),最后只需.doubleValue()即可得到较为精确的double类型的计算结果了。

由此可以编写一个用于浮点型计算的工具类,专职浮点型计算工作。

 import java.math.BigDecimal;

 /**
* 浮点型较为精确计算工具类
*/
public class CalculateUtil {
/**
* 私有构造
*/
private CalculateUtil(){}
/**
* 加法运算
* @param num1 被加数
* @param num2 加数
* @return 两数之和
*/
public static double add(double num1, double num2){
BigDecimal b1 = new BigDecimal(Double.toString(num1));
BigDecimal b2 = new BigDecimal(Double.toString(num2));
return b1.add(b2).doubleValue();
}
/**
* 减法运算
* @param num1 被减数
* @param num2 减数
* @return 两数之差
*/
public static double sub(double num1, double num2){
BigDecimal b1 = new BigDecimal(Double.toString(num1));
BigDecimal b2 = new BigDecimal(Double.toString(num2));
return b1.subtract(b2).doubleValue();
}
/**
* 乘法运算
* @param num1 被乘数
* @param num2 乘数
* @return 两数之积
*/
public static double mul(double num1, double num2){
BigDecimal b1 = new BigDecimal(Double.toString(num1));
BigDecimal b2 = new BigDecimal(Double.toString(num2));
return b1.multiply(b2).doubleValue();
}
/**
* 除法运算(小数点后10位)
* @param num1 被除数
* @param num2 除数
* @return 两数之商
*/
public static double div(double num1, double num2){
return div(num1, num2, 10);
} /**
* 除法运算
* @param num1 被除数
* @param num2 除数
* @param scale 小数点后精度位数
* @return 两数之商
*/
public static double div(double num1, double num2, int scale){
if(scale<0)
throw new IllegalArgumentException("The scale must be a positive integer or zero");
BigDecimal b1 = new BigDecimal(Double.toString(num1));
BigDecimal b2 = new BigDecimal(Double.toString(num2));
return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
}
/**
* 四舍五入
* @param num 需要四舍五入的数
* @param scale 小数点后精度位数
* @return 四舍五入值
*/
public static double round(double num, int scale){
if(scale<0)
throw new IllegalArgumentException("The scale must be a positive integer or zero");
BigDecimal b1 = new BigDecimal(Double.toString(num));
return b1.divide(new BigDecimal("1"), scale, BigDecimal.ROUND_HALF_UP).doubleValue();
} }

最新文章

  1. Android-内存泄漏
  2. tomcat 配置ssi
  3. Mysql 分区处理NULL的得方式
  4. Node.js系列之node.js初探
  5. dubbo-admin-2.5.3 运行报错: Bean property &#39;URIType&#39; is not writable or has an invalid 解决方法
  6. UITextField属性
  7. HDU 1848
  8. Ubuntu下adb的安装
  9. 简单快速的开发框架-SRF
  10. 【POJ2912】【并查集】Rochambeau
  11. 今年暑假不AC(贪心)
  12. InfoQ文章
  13. 【实验室笔记】C#上位机学习笔记
  14. 吐槽net下没有靠谱的FastDFS的sdk之使用thrift实现JAVA和C#互通
  15. Mysql大量插入数据时SQL语句的优化
  16. Java示例:如何执行进程并读取输出
  17. SecureRandom
  18. codeforces 879c
  19. 《图说VR入门》——DK2入门及其资源汇总
  20. valgrind使用简介

热门文章

  1. django快速实现完整登录系统,把登陆注册串在一起并增加cookie(六)
  2. 2个最好的JavaScript编辑器 必须要知道
  3. 安装docker以及常规操作
  4. map,实现技巧,id
  5. laravel 链式组合查询数据
  6. QQ输入法中英文标点符号快速切换
  7. POJ2513:Colored Sticks(字典树+欧拉路径+并查集)
  8. 关于go module
  9. 前端知识扫盲-VUE知识篇一(VUE核心知识)
  10. LC 970. Powerful Integers