double转换为int以及浮点型相加损失精度问题
最近在做支付相关模块的业务,数据库字段却使用的是double类型,其实也行,只要计算不在sql语句中进行,也是没有问题的。
预先的类属性设置的是Double类型,自己算的时候发现小数相加会出现损失精度的情况
如下情形
@Test
public void testDouble(){
Double [] arr = {39.9d,50d,198d,39.9d};
Double verify = 0d;
for (Double aDouble : arr) {
verify += aDouble ;
}
System.out.println(verify);
}
输出的结果是:327.79999999999995 理应为:328
待着疑惑试了下js
一看是一个效果,精度都会缺失。百度了下,解释如下
- 符号位(Sign): 0代表正,1代表为负
- 指数位(Exponent):用于存储科学计数法中的指数数据,并且采用移位存储
- 尾数部分(Mantissa):尾数部分
实数符号位 指数符号位 指数位 有效数位
但是,在将十进制浮点数转换为二进制浮点数时,小数的二进制有时也是不可能精确的,就如同十进制不能准确表示1/3,二进制也无法准确表示1/10,而double类型存储尾数部分最多只能存储52位,于是,计算机在存储该浮点型数据时,便出现了精度丢失。
例如,11.9的内存存储大约为:1011. 1110011001100110011001100...
而在进行浮点类数据计算的时候,浮点参与计算,会左移或右移n位,直到小数点移动到第一个有效数字的右边。于是11.9在转化为二进制后 小数点左移3位,就得到1. 011 11100110011001100110(精度丢失2)
于是最终浮点型运算出现了精度丢失误差。
解决方式,java中可以使用 BigDecimal 来解决
@Test
public void test3(){
System.out.println(0.11+2001299.32);//非精确的输出
BigDecimal bigDecimal1 = new BigDecimal(Double.toString(0.11));
BigDecimal bigDecimal2 = new BigDecimal(Double.toString(2001299.32));
System.out.println(bigDecimal1.add(bigDecimal2));//精确的输出
}
这种方式可以解决,并且很完美,但是还有一种方式比较容易些,毕竟是金额,小数位只有两位,可以先将其扩大100倍,再进行计算,计算完毕之后再除100,也可以解决(这么不要脸的方式也只有我这么low的程序员使用了),惭愧,出错了。
在计算的过程总遇到double转int的情况,总结了下实现的方式
Double d = 1.7d; @Test public void test1(){ // 这样会报错,因为double转换为字符串之后有. System.out.println(Integer.parseInt(d.toString())); } @Test public void test2_1(){ // double 类型可以直接转为int类型 double dd = 1.1; int ddd = (int)dd; System.out.println(ddd); } @Test public void test2_2(){ // 可以通过强转进行转换,Double是包装类,不能直接进行强转,可以拆箱之后再次强转。 int aa = (int)(Double.parseDouble(d.toString())); System.out.println(aa); } @Test public void test3(){ // 这种方式最为简单 System.out.println(d.intValue()); } @Test public void test4(){ DecimalFormat format1 = new DecimalFormat("#"); String s = format1.format(d); System.out.println(s); } @Test public void test5(){ DecimalFormat format1 = new DecimalFormat("#.#"); String s = format1.format(1.35); System.out.println(s); } @Test public void test6(){ DecimalFormat format1 = new DecimalFormat("0.000"); String s = format1.format(1.35); System.out.println(s); }
DecimalFormat 要四舍五入需要加上 setRoundingMode(RoundingMode.HALF_UP); 网上是这么说的,但是自己测试默认就会四舍五入
特殊字符说明
“0” 指定位置不存在数字则显示为0 123.123 ->0000.0000 ->0123.1230
“#” 指定位置不存在数字则不显示 123.123 -> ####.#### ->123.123
“.” 小数点
“%” 会将结果数字乘以100 后面再加上% 123.123 ->#.00->1.3212%
嗯,就这么个坑了。