Java中数值的加减乘除结果都是对的么?丨【奔跑吧!JAVA】
计算机程序内部都是二进制表示的,日常的编程实战中,除了处理字符串外,大部分情况下,都在处理数值的计算,如计算多个值的和或平均值。在Java语言体系中,关于数值的类型有int,float,double,long等。
对于有小数的数值,开发人员首先想到的是可以用float或者double表示。比如求1.0和0.42两个值的差,那么心算可得0.58,但是如果用Java来实际计算一下,那么其结果可能和我们预期的是不一样的。
下面结合特殊场景,来给出Java常见的数值加减乘除运算示例,看看结果都是对的么?具体的示例代码如下:
package com.jyd;
//Java 数值计算精度问题探讨示例
public class NumDemo {
public static void main(String[] args) {
int a = 10;
int c0 = a / 3 ;
System.out.println(c0);//3,只保留整数
//float和double大部分情况精确的,但有时会计算错误
// 在商业计算上(如金融、银行,财务),不建议使用。
float b = 0.1f;
float c2 = b / 3.0f;//0.033333335
System.out.println(c2);
float b2 = 20019999;
System.out.println(b2); //2.002E7
double d = 45.9D;
double c3 = d / 3.0D ;
System.out.println(c3);//15.299999999999999 (not 15.3)
double e = 2001299.32D;
double e2 = 0.11D;
System.out.println(e + e2); //2001299.4300000002
System.out.println(1.0 - 0.42);//0.5800000000000001
System.out.println(4.015 * 100);//401.49999999999994
System.out.println(0.01 + 0.05);//0.060000000000000005
}
}
上述示例代码中,int类型的数值10,/ 除以3,实际返回的是int类型,即3,只保留整数部分。这个计算结果只要我们知道,一般都可以理解。Java中的浮点类型(如float和double)采用 IEEE 754 标准。float类型的值0.1除以3.0时,返回的还是float类型的值,按照数学角度来说,应该返回0.3333...(循环),但是Java程序返回0.033333335。float类型长度为32位,其中用 1 位表示数字的符号,用 8 位表示指数,用 23 位表示小数部分。感觉这个长度有的长,但是实际使用起来精度还是不够。即使是64位的double也是如此。
示例中,20019999这个float类型的值,输出后却是2.002E7,即20020000,真是奇乎怪哉!,如果这个是银行账户的金额,1个账户多1元,那么几亿个户头则多出了几个亿的金额,足见这个计算精度的问题是在特殊场景下,是一个不可接受的方案。 其他的示例涉及到加法、减法、乘法,也同样会出现计算错误的问题。
按照官网的说法,float和double一般用于工程计算,而不能应用商业计算,如银行,财务等。那么商业计算上,该如何解决此问题呢?此时需要用到一个特殊的类型,为BigDecimal。此类型是一个精确的类型,下面给出示例:
package com.jyd;
import java.math.BigDecimal;
import java.math.MathContext;
//Java 数值计算精度问题探讨示例
public class BigNumDemo {
public static void main(String[] args) {
BigDecimal b = new BigDecimal("0.1");
BigDecimal b1 = new BigDecimal("3.0");
BigDecimal c2 = b.divide(b1,10,BigDecimal.ROUND_DOWN) ;//0.0333333333
System.out.println(c2);
BigDecimal b2 = new BigDecimal(""+20019999);
System.out.println(b2); //20019999
BigDecimal d = new BigDecimal("45.9");
BigDecimal d2 = new BigDecimal("3.0");
BigDecimal c3 = d.divide(d2,10,BigDecimal.ROUND_CEILING) ;
System.out.println(c3);//15.3000000000
BigDecimal e = new BigDecimal("2001299.32");
BigDecimal e2 = new BigDecimal("0.11");
System.out.println(e.add(e2)); //2001299.43
BigDecimal e3 = new BigDecimal("1.0");
BigDecimal e4 = new BigDecimal("0.42");
System.out.println(e3.subtract(e4));//0.58
BigDecimal e5 = new BigDecimal("4.015");
BigDecimal e6 = new BigDecimal("100");
System.out.println(e5.multiply(e6, MathContext.UNLIMITED));//401.500
BigDecimal e7 = new BigDecimal("0.01");
BigDecimal e8 = new BigDecimal("0.05");
System.out.println(e7.add(e8));//0.06
BigDecimal e9 = new BigDecimal("999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999");
System.out.println(e9.add(e3));//1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0
}
}
BigDecimal有多种构造函数,常用的有2种。建议使用String构造方式,不建议使用double构造方式,可能会出现精度丢失问题。通过上述示例可以看出,商业计算应该使用 BigDecimal类型来完成,虽然在加减乘除这块使用起来有点不够简便,但是仍然比较容易理解。且操作字符串,也更容易操作。在乘除计算中,强制需要制定额外的参数,比如明确小数个数,四舍五入的模式等。
最后,Java编程的细节还有很多,比如不同精度的数值的赋值和转换问题,float和double为什么会导致数据精度的丢失等,可以参考官网文档或其他资料。这里不再赘述。
【奔跑吧!JAVA】有奖征文火热进行中:https://bbs.huaweicloud.cn/blogs/265241
- 点赞
- 收藏
- 关注作者
评论(0)