今天程序出了一个BUG,偶尔会出现订单与实际差了一分钱的情况。
因为我们的数额是精确到小数点后三位,但订单只需要两位,于是我就对BigDecimal做了个测试。
public static void main(String[] args) {
float elect = 3.155f;
System.out.println(new BigDecimal(elect).setScale(2, BigDecimal.ROUND_HALF_UP).toString());
}
输出结果理想情况应该为 3.16,可实际情况是
不科学,又试验了多次,
public static void main(String[] args) {
float elect = 3.105f;
for (int i = 0; i < 10; i++) {
System.out.println("elect:"+elect+"---------format:"+new BigDecimal(elect).setScale(2, BigDecimal.ROUND_HALF_UP).toString());
elect = elect + 0.01f;
}
}
elect:3.105---------format:3.11
elect:3.115---------format:3.12
elect:3.125---------format:3.13
elect:3.135---------format:3.13
elect:3.145---------format:3.14
elect:3.155---------format:3.15
elect:3.165---------format:3.16
elect:3.175---------format:3.17
elect:3.185---------format:3.18
elect:3.195---------format:3.19
都没生效,换个类型试一下,用double
public static void main(String[] args) {
double elect = 3.105d;
for (int i = 0; i < 10; i++) {
System.out.println("elect:"+elect+"---------format:"+new BigDecimal(elect).setScale(2, BigDecimal.ROUND_HALF_UP).toString());
elect = elect + 0.01d;
}
结果不尽人意
elect:3.105---------format:3.10
elect:3.1149999999999998---------format:3.11
elect:3.1249999999999996---------format:3.12
elect:3.1349999999999993---------format:3.13
elect:3.144999999999999---------format:3.14
elect:3.154999999999999---------format:3.15
elect:3.1649999999999987---------format:3.16
elect:3.1749999999999985---------format:3.17
elect:3.1849999999999983---------format:3.18
elect:3.194999999999998---------format:3.19
Java中的简单浮点数类型float和double不能够进行运算,会因为进制转换
而导致精准度错误,这个不在这次讨论范围。
改一下程序:
public static void main(String[] args) {
List<Double> ele=new ArrayList<>();
ele.add(3.105d);
ele.add(3.115d);
ele.add(3.125d);
ele.add(3.135d);
ele.add(3.145d);
ele.add(3.155d);
ele.add(3.165d);
ele.add(3.175d);
ele.add(3.185d);
ele.add(3.195d);
double elect = 3.105d;
for (int i = 0,length=ele.size(); i < length; i++) {
System.out.println("elect:"+ele.get(i)+"---------format:"+new BigDecimal(ele.get(i)).setScale(2, BigDecimal.ROUND_HALF_UP).toString());
}
}
输出结果依然很奇怪:
elect:3.105---------format:3.10
elect:3.115---------format:3.12
elect:3.125---------format:3.13
elect:3.135---------format:3.13
elect:3.145---------format:3.15
elect:3.155---------format:3.15
elect:3.165---------format:3.17
elect:3.175---------format:3.17
elect:3.185---------format:3.19
elect:3.195---------format:3.19
可见部分成功,部分失败,也就是说double依然是不可以
用String试一下
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
String elect = "3.1"+i+5;
System.out.println("elect:" + elect + "---------format:" + new BigDecimal(elect).setScale(2, BigDecimal.ROUND_HALF_UP).toString());
}
}
结果可喜:
elect:3.105---------format:3.11
elect:3.115---------format:3.12
elect:3.125---------format:3.13
elect:3.135---------format:3.14
elect:3.145---------format:3.15
elect:3.155---------format:3.16
elect:3.165---------format:3.17
elect:3.175---------format:3.18
elect:3.185---------format:3.19
elect:3.195---------format:3.20
全部正确。
在《Effective Java》这本书中也提到这个原则,float和double只能用来做科学计算或者是工程计算,在商业计算中我们要用 java.math.BigDecimal。
所以我们需要金额或者相关数据精准度计算是一定要用String类型来计算
回到最初的程序,float计算,转成String
public static void main(String[] args) {
float elect = 3.155f;
System.out.println(new BigDecimal(String.valueOf(elect)).setScale(2, BigDecimal.ROUND_HALF_UP).toString());
}
结果就是正确的了
3.16
OVER。