文章目录

  • 浮点数精度丢失
  • 如何用BigDecimal解决double精度问题?
  • But
  • BigDecimal工具类进行运算,示例:
  • Double的加减乘除运算工具类2
  • 阿里巴巴Java开发手册关于BigDecimal的规定

浮点数精度丢失

精度丢失的问题是在其他计算机语言中也都会出现,float和double类型的数据在执行二进制浮点运算的时候,并没有提供完全精确的结果。产生误差不在于数的大小,而是因为数的精度。

如何用BigDecimal解决double精度问题?

我们已经明白为什么精度会存在丢失现象,那么我们就应该知道,当某个业务场景对double数据的精度要求非常高时,就必须采取某种手段来处理这个问题,这也是BigDecimal为什么会被广泛应用于金额支付场景中的原因啦。

BigDecimal类位于java.math包下,用于对超过16位有效位的数进行精确的运算。一般来说,double类型的变量可以处理16位有效数,但实际应用中,如果超过16位,就需要BigDecimal类来操作。

But

public static void main(String[] args) {// 方法1BigDecimal a = new BigDecimal(0.1);System.out.println("a --> " + a);// 方法2BigDecimal b = new BigDecimal("0.1");System.out.println("b --> " + b);// 方法3BigDecimal c = BigDecimal.valueOf(0.1);System.out.println("c --> " + c);}

控制台却输出

a --> 0.1000000000000000055511151231257827021181583404541015625
b --> 0.1
c --> 0.1

可以看到,使用方法一的构造函数仍然出现了精度丢失的问题,而方法二和方法三符合我们的预期,为什么会这样呢?

这三个方法其实对应着三种不同的构造函数:

public BigDecimal(double val) {this(val,MathContext.UNLIMITED);}public BigDecimal(String val) {this(val.toCharArray(), 0, val.length());}public static BigDecimal valueOf(double val) {// Reminder: a zero double returns '0.0', so we cannot fastpath// to use the constant ZERO.  This might be important enough to// justify a factory approach, a cache, or a few private// constants, later.return new BigDecimal(Double.toString(val)); // 可以看出其实第三种实际上就是第二种的封装}

关于这三个构造函数,JDK已经给出了解释,并用Notes标注:

BigDecimal工具类进行运算,示例:

​ 转换为BigDecimal对象之后再进行加减乘除操作,这样精度就不会出现问题了。这也是为什么有关金钱数据存储都使用BigDecimal。

​ 处理double类型数据的加、减、乘、除运算时,使用如下方法:

import java.math.BigDecimal;public class BigDecimalUtil {/*** 加法运算* @param m1* @param m2* @return  不加doubleValue()则, 返回BigDecimal对象*/public static double addDouble(double m1, double m2) {BigDecimal p1 = new BigDecimal(Double.toString(m1));BigDecimal p2 = new BigDecimal(Double.toString(m2));return p1.add(p2).doubleValue();}/*** 减法运算* @param m1* @param m2* @return  不加doubleValue()则, 返回BigDecimal对象*/public static double subDouble(double m1, double m2) {BigDecimal p1 = new BigDecimal(Double.toString(m1));BigDecimal p2 = new BigDecimal(Double.toString(m2));return p1.subtract(p2).doubleValue();}/*** 乘法运算* @param m1* @param m2* @return  不加doubleValue()则, 返回BigDecimal对象*/public static double mul(double m1, double m2) {BigDecimal p1 = new BigDecimal(Double.toString(m1));BigDecimal p2 = new BigDecimal(Double.toString(m2));return p1.multiply(p2).doubleValue();}/*** 除法运算* @param   m1* @param   m2* @param   scale* @return  不加doubleValue()则, 返回BigDecimal对象*/public static double div(double m1, double m2, int scale) {if (scale < 0) {throw new IllegalArgumentException("Parameter error");}BigDecimal p1 = new BigDecimal(Double.toString(m1));BigDecimal p2 = new BigDecimal(Double.toString(m2));return p1.divide(p2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();}
}

Double的加减乘除运算工具类2

​ BigDecimal所创建的是对象,故我们不能使用传统的+、-、*、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。

​ 方法中的参数也必须是BigDecimal的对象。

​ 网上有很多这样的工具类,这边直接贴一下,逻辑不难,主要为了简化项目中频繁互相转化的问题。

import java.math.BigDecimal;/*** * 用于高精确处理常用的数学运算* <p>*/public class ArithmeticUtils {//默认除法运算精度private static final int DEF_DIV_SCALE = 10;/*** * 提供精确的加法运算* <p>* ** <p>* * @param v1 被加数* <p>* * @param v2 加数* <p>* * @return 两个参数的和* <p>*/public static double add(double v1, double v2) {BigDecimal b1 = new BigDecimal(Double.toString(v1));BigDecimal b2 = new BigDecimal(Double.toString(v2));return b1.add(b2).doubleValue();}/*** * 提供精确的加法运算* <p>* ** <p>* * @param v1 被加数* <p>* * @param v2 加数* <p>* * @return 两个参数的和* <p>*/public static BigDecimal add(String v1, String v2) {BigDecimal b1 = new BigDecimal(v1);BigDecimal b2 = new BigDecimal(v2);return b1.add(b2);}/*** * 提供精确的加法运算* <p>* ** <p>* * @param v1 被加数* <p>* * @param v2 加数* <p>* * @param scale 保留scale 位小数* <p>* * @return 两个参数的和* <p>*/public static String add(String v1, String v2, int scale) {if (scale < 0) {throw new IllegalArgumentException("The scale must be a positive integer or zero");}BigDecimal b1 = new BigDecimal(v1);BigDecimal b2 = new BigDecimal(v2);return b1.add(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();}/*** 提供精确的减法运算** @param v1 被减数* @param v2 减数* @return 两个参数的差*/public static double sub(double v1, double v2) {BigDecimal b1 = new BigDecimal(Double.toString(v1));BigDecimal b2 = new BigDecimal(Double.toString(v2));return b1.subtract(b2).doubleValue();}/*** 提供精确的减法运算。** @param v1 被减数* @param v2 减数* @return 两个参数的差*/public static BigDecimal sub(String v1, String v2) {BigDecimal b1 = new BigDecimal(v1);BigDecimal b2 = new BigDecimal(v2);return b1.subtract(b2);}/*** 提供精确的减法运算** @param v1    被减数* @param v2    减数* @param scale 保留scale 位小数* @return 两个参数的差*/public static String sub(String v1, String v2, int scale) {if (scale < 0) {throw new IllegalArgumentException("The scale must be a positive integer or zero");}BigDecimal b1 = new BigDecimal(v1);BigDecimal b2 = new BigDecimal(v2);return b1.subtract(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();}/*** 提供精确的乘法运算** @param v1 被乘数* @param v2 乘数* @return 两个参数的积*/public static double mul(double v1, double v2) {BigDecimal b1 = new BigDecimal(Double.toString(v1));BigDecimal b2 = new BigDecimal(Double.toString(v2));return b1.multiply(b2).doubleValue();}/*** 提供精确的乘法运算** @param v1 被乘数* @param v2 乘数* @return 两个参数的积*/public static BigDecimal mul(String v1, String v2) {BigDecimal b1 = new BigDecimal(v1);BigDecimal b2 = new BigDecimal(v2);return b1.multiply(b2);}/*** 提供精确的乘法运算** @param v1    被乘数* @param v2    乘数* @param scale 保留scale 位小数* @return 两个参数的积*/public static double mul(double v1, double v2, int scale) {BigDecimal b1 = new BigDecimal(Double.toString(v1));BigDecimal b2 = new BigDecimal(Double.toString(v2));return round(b1.multiply(b2).doubleValue(), scale);}/*** 提供精确的乘法运算** @param v1    被乘数* @param v2    乘数* @param scale 保留scale 位小数* @return 两个参数的积*/public static String mul(String v1, String v2, int scale) {if (scale < 0) {throw new IllegalArgumentException("The scale must be a positive integer or zero");}BigDecimal b1 = new BigDecimal(v1);BigDecimal b2 = new BigDecimal(v2);return b1.multiply(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();}/*** 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到* <p>* 小数点以后10位,以后的数字四舍五入** @param v1 被除数* @param v2 除数* @return 两个参数的商*/public static double div(double v1, double v2) {return div(v1, v2, DEF_DIV_SCALE);}/*** 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指* <p>* 定精度,以后的数字四舍五入** @param v1    被除数* @param v2    除数* @param scale 表示表示需要精确到小数点以后几位。* @return 两个参数的商*/public static double div(double v1, double v2, int scale) {if (scale < 0) {throw new IllegalArgumentException("The scale must be a positive integer or zero");}BigDecimal b1 = new BigDecimal(Double.toString(v1));BigDecimal b2 = new BigDecimal(Double.toString(v2));return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();}/*** 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指* <p>* 定精度,以后的数字四舍五入** @param v1    被除数* @param v2    除数* @param scale 表示需要精确到小数点以后几位* @return 两个参数的商*/public static String div(String v1, String v2, int scale) {if (scale < 0) {throw new IllegalArgumentException("The scale must be a positive integer or zero");}BigDecimal b1 = new BigDecimal(v1);BigDecimal b2 = new BigDecimal(v1);return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).toString();}/*** 提供精确的小数位四舍五入处理** @param v     需要四舍五入的数字* @param scale 小数点后保留几位* @return 四舍五入后的结果*/public static double round(double v, int scale) {if (scale < 0) {throw new IllegalArgumentException("The scale must be a positive integer or zero");}BigDecimal b = new BigDecimal(Double.toString(v));return b.setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue();}/*** 提供精确的小数位四舍五入处理** @param v     需要四舍五入的数字* @param scale 小数点后保留几位* @return 四舍五入后的结果*/public static String round(String v, int scale) {if (scale < 0) {throw new IllegalArgumentException("The scale must be a positive integer or zero");}BigDecimal b = new BigDecimal(v);return b.setScale(scale, BigDecimal.ROUND_HALF_UP).toString();}/*** 取余数** @param v1    被除数* @param v2    除数* @param scale 小数点后保留几位* @return 余数*/public static String remainder(String v1, String v2, int scale) {if (scale < 0) {throw new IllegalArgumentException("The scale must be a positive integer or zero");}BigDecimal b1 = new BigDecimal(v1);BigDecimal b2 = new BigDecimal(v2);return b1.remainder(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();}/*** 取余数 BigDecimal** @param v1    被除数* @param v2    除数* @param scale 小数点后保留几位* @return 余数*/public static BigDecimal remainder(BigDecimal v1, BigDecimal v2, int scale) {if (scale < 0) {throw new IllegalArgumentException("The scale must be a positive integer or zero");}return v1.remainder(v2).setScale(scale, BigDecimal.ROUND_HALF_UP);}/*** 比较大小* <p>* 阿里巴巴开发规范明确:比较BigDecimal的等值需要使用compareTo,不可用equals* <p>* equals会比较值和精度,compareTo会忽略精度** @param v1 被比较数* @param v2 比较数* @return 如果v1 大于v2 则 返回true 否则false*/public static boolean compare(String v1, String v2) {BigDecimal b1 = new BigDecimal(v1);BigDecimal b2 = new BigDecimal(v2);int bj = b1.compareTo(b2);boolean res;if (bj > 0) {res = true;} else {res = false;}return res;}}

阿里巴巴Java开发手册关于BigDecimal的规定

【强制】如上所示BigDecimal的等值比较应使用compareTo()方法,而不是equals()方法。 说明:equals()方法会比较值和精度(1.0和1.00返回结果为false),而compareTo()则会忽略精度。

关于这一点,我们来看一个例子就明白了:

public static void main(String[] args) {BigDecimal a = new BigDecimal("1");BigDecimal b = new BigDecimal("1.0");System.out.println(a.equals(b)); // falseSystem.out.println(a.compareTo(b)); //0 表示相等}

JDK中对这两个方法的解释是这样的:

  • 使用compareTo方法,两个值相等但是精度不同的BigDecimal对象会被认为是相等的,比如2.0和2.00。建议使用x.compareTo(y) 0来表示(<, == , > , >= , != , <=)中的其中一个关系,就表示运算符。
  • equals方法与compareTo方法不同,此方法仅在两个BigDecimal对象的值和精度都相等时才被认为是相等的,如2.0和2.00就是不相等的。

的等值比较应使用compareTo()方法,而不是equals()方法。 说明:equals()方法会比较值和精度(1.0和1.00返回结果为false),而compareTo()则会忽略精度。

关于这一点,我们来看一个例子就明白了:

public static void main(String[] args) {BigDecimal a = new BigDecimal("1");BigDecimal b = new BigDecimal("1.0");System.out.println(a.equals(b)); // falseSystem.out.println(a.compareTo(b)); //0 表示相等}

JDK中对这两个方法的解释是这样的:

  • 使用compareTo方法,两个值相等但是精度不同的BigDecimal对象会被认为是相等的,比如2.0和2.00。建议使用x.compareTo(y) 0来表示(<, == , > , >= , != , <=)中的其中一个关系,就表示运算符。
  • equals方法与compareTo方法不同,此方法仅在两个BigDecimal对象的值和精度都相等时才被认为是相等的,如2.0和2.00就是不相等的。

利用BigDecimal类巧妙处理Double类型精度丢失相关推荐

  1. double类型精度丢失问题以及解决方法

    double类型精度丢失问题: (1)加法运算. public static void main(String[] args) {double number1 = 1;double number2 = ...

  2. java中double类型精度丢失问题及解决方法

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 来源: https://blog.csdn.net/yacolsp ...

  3. double operator[](int i)_java中double类型精度丢失问题及解决方法

    原文链接:https://blog.csdn.net/yacolspace/article/details/78287394 double类型数据加减操作精度丢失问题 今天在项目中用到double类型 ...

  4. Java中double类型精度丢失的问题_double类型数据加减操作精度丢失解决方法_BigDecimal取整

    BigDecimal在用double做入参的时候,二进制无法精确地表示十进制小数,编译器读到字符串"0.0000002"和"1.0000002"之后,必须把它转 ...

  5. Double计算精度丢失(金融入门知识点)

    Double计算精度丢失(金融入门知识点) 一.double精度丢失 二.为什么double会精度丢失 三.BigDecimal错误的用法 四.BigDecimal正确的用法 Double计算精度丢失 ...

  6. 使用 MyBatis 实体类里的 Double 类型查询不到值

    使用 MyBatis 实体类里的 double 类型查询不到值.查询实体类时,里面的 Integer 和 String 类型都可以查询到,update 各个属性也都可以修改成功. 因为 MyBatis ...

  7. double java 精度丢失_java中double和float精度丢失问题及解决方法

    在讨论两位double数0.2和0.3相加时,毫无疑问他们相加的结果是0.5.但是问题总是如此吗? 下面我们让下面两个doubles数相加,然后看看输出结果: @Test public void te ...

  8. Java接口long类型精度丢失,解决前后端交互Long类型精度丢失问题

    雪花算法ID,对应的后端Long类型,前端number类型,它们的精度不一样,导致精度丢失 现象 雪花算法得到的ID较长,传到前端后,精度丢失 库中:23754851322302474 后端:2375 ...

  9. 解决前后端交互Long类型精度丢失的问题

    雪花算法ID,对应的后端Long类型,前端number类型,它们的精度不一样,导致精度丢失 文章目录 一.现象与分析 1.1. 现象 1.2. 分析 二.解决方案 2.1. 方法一单个注解 2.2. ...

  10. Double类型精度问题引起的错误

    场景说明 研发同事让把某个double类型字段的值四舍五入保留2位小数,mysql中round(col,2)可以实现四舍五入并且保留2位小数,但是神奇的事情发生了:发现有的四舍五入是正确的,而有的不是 ...

最新文章

  1. php 无限查找下级业绩_php如何查找会员无限分类的所有上级和所有下级
  2. linux shutter截图,Ubuntu中安装Shutter截图工具
  3. C#服务器端获取客户端(html)控件值
  4. java 置顶_[置顶]java开发之基础篇2
  5. ***客户端出现“无法完成连接尝试”的解决方法
  6. 川大计算机文化基础在线作业,川大1309《计算机文化基础0008》在线作业2答案.docx...
  7. iphone退款申请教程_【揭秘】朋友圈卖的iOS退款、王者荣耀0元撸点券教程
  8. 我的docker随笔15:MySQL启动时自动创建数据库
  9. elasticsearch 清理数据后硬盘不释放_电脑C盘正确的5种清理方法,你还不知道?学会后轻松释放20G空间...
  10. 顶尖技术专家严选,15场前沿论坛思辨,2019中国大数据技术大会邀您共赴!
  11. 142.PHP session 阻塞问题
  12. 两周搞定计算机专业毕业设计,附源码+论文+答辩
  13. 加州大学洛杉机分校计算机科学,加州大学洛杉矶分校计算机科学排名第14(2020年TFE美国排名)...
  14. allegro174的brd转alg文件导入AD异常
  15. background-size属性详解
  16. 2021-07-22
  17. windows的文件路径文件(夹)名的分割符也可以用正斜杠
  18. css实现一个温度计图表
  19. jar包冲突java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException
  20. 使用html5 canvas 绘制Android机器人

热门文章

  1. 使用 K3S 创建本地开发集群
  2. mysql和redis常见面试题_redis相关面试题
  3. 矢量网络分析仪(矢网)组成和原理简介
  4. 台式计算机拆卸步骤,拆装台式电脑主机的方法图解步骤
  5. php里用钢笔画曲线,PS如何使用钢笔工具进行抠图
  6. chrome刷新缓存
  7. origin绘图同时添加柱状图和折线图
  8. vue微信公众号开发h5授权登录
  9. 吴恩达深度学习作业之deepleraning_L1W2_h2
  10. POI设置导出的EXCEL锁定指定的单元格