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
⼀看是⼀个效果,精度都会缺失。百度了下,解释如下
产⽣原因:
计算机并不能识别除了⼆进制数据以外的任何数据,⽆论我们使⽤何种编程语⾔,在何种编译环境下⼯作,都要先把源程序翻译成⼆进制的机器码后才能被计算机识别。
⽽在存储浮点型数据时,会分为三部分进⾏存储:
1. 符号位(Sign): 0代表正,1代表为负
2. 指数位(Exponent):⽤于存储科学计数法中的指数数据,并且采⽤移位存储
3. 尾数部分(Mantissa):尾数部分
其中前两位分别为实数符号位和指数符号位,于是存储结构实际为如下:
31 30 29----23 22----0
实数符号位指数符号位指数位有效数位
但是,在将⼗进制浮点数转换为⼆进制浮点数时,⼩数的⼆进制有时也是不可能精确的,就如同⼗进制不能准确表⽰1/3,⼆进制也⽆法准确表⽰1/10,⽽double类型存储尾数部分最多只能存储52位,于是,计算机在存储该浮点型数据时,便出现了精度丢失。
例如,11.9的内存存储⼤约为:1011.
⽽在进⾏浮点类数据计算的时候,浮点参与计算,会左移或右移n位,直到⼩数点移动到第⼀个有效数字的右边。于是11.9在转化为⼆进制后⼩数点左移3位,就得到1. 011 11100110011001100110(精度丢失2)
于是最终浮点型运算出现了精度丢失误差。
解决⽅式,java中可以使⽤BigDecimal来解决
@Test
public void test3(){
System.out.println(0.11+2001299.32);//⾮精确的输出
BigDecimal bigDecimal1 = new String(0.11));
BigDecimal bigDecimal2 = new String(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.String()));
}
@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.String()));
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
“.” ⼩数点
bigdecimal转换为integer“%” 会将结果数字乘以100 后⾯再加上% 123.123 ->#.00->1.3212%
嗯,就这么个坑了。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论