涉及到支付问题,有时相减会出现意外的结果:
public class testPay {
public static void main(String[] args) {
System.out.println(10.00 - 9.60);
}
}
//结果:0.40000000000000036
**System.out.println(10.00 - 9.60); 编译的class文件使用jad反编译后**
System.out.println(0.40000000000000036D);
原因:这是因为在计算机中浮点数有可能(注意是可能)是不准确的,它只能无限接近准确值,而不能完全精确。为什么会如此呢?这是由浮点数的存储规则所决定的,我们先来看0.4这个十进制小数如何转换成二进制小数,使用“乘2取整,顺序排列”法(不懂?这就没招了,太基础了),我们发现0.4不能使用二进制准确的表示,在二进制数世界里它是一个无限循环的小数,也就是说,“展示”都不能“展示”,更别说是在内存中存储了(浮点数的存储包括三部分:符号位、指数位、尾数,具体不再介绍),可以这样理解,在十进制的世界里没有办法准确表示1/3,那在二进制世界里当然也无法准确表示1/5(如果二进制也有分数的话倒是可以表示),在二进制的世界里1/5是一个无限循环小数。
详细可以查看 IEEE二进制浮点数算术标准
表示方法:符号位 + 幂指位 + 数值位
符号位 | 幂指位 | 数值位 | |
---|---|---|---|
float | 1 | 8 | 23 |
double | 1 | 11 | 52 |
0.4
符号位 :0
幂指位:01111111 (默认值)
数值位: 01100110 01100110 0110011
0.4 * 2 = 0.8 0
0.8 * 2 = 1.6 1
0.6 * 2 = 1.2 1
0.2 * 2 = 0.4 0
0.4 * 2 = 0.8 0
…
错误处理:
结果是0.4,看似解决了,大批量数据还是会出现误差。
import java.text.DecimalFormat;
import java.text.NumberFormat;
public class testPay {
public static void main(String[] args) {
// System.out.println(10.00 - 9.60);
NumberFormat f = new DecimalFormat("#.##");
System.out.println(f.format(10.00 - 9.60));
//结果 0.4
}
}
解决方法
A:使用BigDecimal BigDecimal是专门为弥补浮点数无法精确计算的缺憾而设计的类,并且它本身也提供了加减乘除的常用数学算法。Java BigDecimal详解 http://blog.csdn.net/jackiehff/article/details/8582449
Android除法运算,保留小数
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.NumberFormat;
public class testPay {
public static void main(String[] args) {
// System.out.println(10.00 - 9.60);
// NumberFormat f = new DecimalFormat("#.##");
// System.out.println(f.format(10.00 - 9.60)); //结果 0.4
BigDecimal b1 = new BigDecimal(Double.toString(10.00));
BigDecimal b2 = new BigDecimal(Double.toString(9.60));
System.out.println(b1.subtract(b2).doubleValue()); // 结果 0.4
}
}
B:使用整型 把参与运算的值扩大100倍,并转变为整型,然后在展现时再缩小100倍。之前项目上使用的银联支付传的价格单位就是分,用的就是这个方法。
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.NumberFormat;
public class testPay {
public static void main(String[] args) {
// System.out.println(10.00 - 9.60);
// NumberFormat f = new DecimalFormat("#.##");
// System.out.println(f.format(10.00 - 9.60)); //结果 0.4
// 使用BigDecimal
// BigDecimal b1 = new BigDecimal(Double.toString(10.00));
// BigDecimal b2 = new BigDecimal(Double.toString(9.60));
// System.out.println(b1.subtract(b2).doubleValue()); // 结果 0.4
// 扩大100倍 在缩小
System.out.println((10.00 * 100 - 9.60 * 100) / 100);// 结果 0.4
}
}
我是IT小王,如果喜欢我的文章,可以扫码关注我的微信公众号