double计算结果不精确

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了double计算结果不精确相关的知识,希望对你有一定的参考价值。

参考技术A 问题场景:
使用double进行计算时,偶尔会出现计算不准确的现象,比如:398649.9+0.2 = 398650.10000000003

问题原因:
计算机计算的时候数字是用二进制保存的,计算后再转换成十进制,如果精度不够就会出现误差。

解决办法:
decimal是128位高精度浮点数,常用于金融运算,不会出现浮点数计算的误差。
如果项目对执行效率要求高就使用double,如果项目对数字精确度要求高就使用decimal类型(java语言使用BigDecimal)。
BigDecimal的构造函数入参为String类型,分别使用add、subtract、multiply、divide直接进行加减乘除运算,也可以指定运算结果精确到小数点后几位。
1)直接进行加法
public static String add(String v1,String v2)
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.add(b2).toString();

2)指定精确位数的除法
public static String div(String v1,String v2,int scale) 
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).toString();


备注:
js中可以将浮点数转为整数,再对结果做运算。比如0.1 + 0.2,可以转化为(1*2)/3
js四舍五入使用toFixed()方法
js判断两位小数输入的正则表达式为RegExp("^([0-9]1,6)(\\.[0-9]0,2)?$");

BigDecimal

float和double类型的主要为了科学计算和工程计算而设计的,它们执行二进制浮点运算,这时为了在广泛的数值范围上提供较为精确的近似计算而精心设计的。然而,它们没有提供完全精确的结果,所以不应该被用于要求精确结果的场合。通常商业计算往往需要BigDecimal来计算精确要求比较高的数值。比如

 

public static void main(String[] args) {
        System.out.println(0.07 + 0.02);
        System.out.println(0.58 - 0.42);
        System.out.println(1.005 * 100);
        System.out.println(15.1 / 1000);
}
输出的结果为:
0.09000000000000001
0.15999999999999998
100.49999999999999
0.015099999999999999

 

无论是在任何环境下,都需要将代码转为二进制机器码才能让机器识别,当浮点数直接输出时,会保持精度,而当浮点数进行计算后,可能会丢失精度,这时就需要BigDecimal来进行计算。

1.创建BigDecimal

BigDecimal中有一个以双精度浮点数为参数的构造函数,实际上传入浮点数进行计算时还是会精度丢失。

 

 

public static void main(String[] args) {
  

  BigDecimal num1 = new BigDecimal(1.005);
  BigDecimal num2 = new BigDecimal(100);

  BigDecimal num3 = new BigDecimal("1.005");
  BigDecimal num4 = new BigDecimal("100");

  System.out.println(num1.multiply(num2));
  System.out.println(num3.multiply(num4));

} 
输出分别为:
100.49999999999998934185896359849721193313598632812500 100.500

 

 

 

但是可以发现传入字符串时得到了精确的结果,还有BigDecimal.valueOf(double val)也可以得到精确地结果,可以看下面BigDecimal.valueOf(double val)的源码

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));
    }

此方法先是将参数转为了String类型,然后再调用参数为String类型的构造参数,所以在用BigDecimal时,尽量用new BigDecimal(String val) 或 BigDecimal.valueof(double val)来保证得到精确的结果

2.compareTo

BigDecimal比较大小用compareTo()。

 

public static void main(String[] args) {
    System.out.println(BigDecimal.valueOf(0.05).compareTo(BigDecimal.valueOf(0.04)));
    System.out.println(BigDecimal.valueOf(0.04).compareTo(BigDecimal.valueOf(0.04)));
    System.out.println(BigDecimal.valueOf(0.03).compareTo(BigDecimal.valueOf(0.04)));
}  
输出为:
1
0
-1

 

BigDecimal.compareTo(BigDecimal val)

当BigDecimal比val大时返回1,

当BigDecimal小于val时返回-1,

当BigDecimal等于val时返回0。

 

以上是关于double计算结果不精确的主要内容,如果未能解决你的问题,请参考以下文章

Java中小数精确计算

使用BigDecimal来进行精确计算

Java精确计算

bigDecimal学习

BigDecimal

Java BigDecimal使用