今天的博客主题
基础篇 --》常用类 --》Math & Number类
Math类
Java 的 Math 包含了用于执行基本数学运算的属性和方法,如初等指数、对数、平方根和三角函数。
Math 的方法都被定义为 static 形式,通过 Math 类可以直接调用。
官方文档:https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html
Math常用方法
public class MathDemo {
public static void main(String[] args) {
//随机数(取出来的是小于1的 double 型随机数字)
System.out.println(Math.random());
//向上取整(不管小数点后面是多少,直接进1)
System.out.println(Math.ceil(11.01)); // 12.0
System.out.println(Math.ceil(11.99)); // 12.0
//向下取整(不管小数点后面是多少,直接舍去)
System.out.println(Math.floor(11.01)); // 11.0
System.out.println(Math.floor(11.99)); // 11.0
//四舍五入(正常数学里的四舍五入)
System.out.println(Math.round(11.51)); // 12
System.out.println(Math.round(11.49)); // 11
//获取圆周率
System.out.println(Math.PI); // 3.141592653589793
//绝对值(不管正负,得到的总是正数,0比较特殊,得到的就是0)
System.out.println(Math.abs(-10.11)); // 10.11
System.out.println(Math.abs(10.11)); // 10.11
// 计算平方根
System.out.println(Math.sqrt(16)); // 4.0
//计算立方根
System.out.println(Math.cbrt(8)); // 2.0
//计算a的b次方
System.out.println(Math.pow(3,2)); // 9.0
//取出最大值
System.out.println(Math.max(11.99, 12.0)); // 12.0
//取出最小值
System.out.println(Math.min(11.99, 12.0)); // 11.99
}
}
Number类
Number类是java.lang包下的一个抽象类,提供了将包装类型拆箱成基本类型的方法,所有基本类型的包装类型都继承了该抽象类,并且是final声明不可继承改变
需要使用数字的时候,我们通常使用内置数据类型,如:byte、int、long、double 等。
然而,在实际开发过程中,我们经常会遇到需要使用对象,而不是内置数据类型的情形。为了解决这个问题,Java 语言为每一个内置数据类型提供了对应的包装类。
Integer、Long、Byte、Double、Float、Short)都是抽象类 Number 的子类。
是由编译器特别支持的包装称为装箱,所以当内置数据类型被当作对象使用的时候,编译器会把内置类型装箱为包装类。相似的,编译器也可以把一个对象拆箱为内置类型。
对于包装数据类型的理解参考《02.编程基础-->3.Java数据类型》
BigDecimal类
BigDecimal类是在java.math包中提供的API,用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数。在实际应用中,需要对更大或者更小的数进行运算和处理。BigDecimal所创建的是对象,我们不能使用传统的+、-、*、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是BigDecimal的对象。构造器是类的特殊方法,专门用来创建对象,特别是带有参数的对象。通过结论可看出float和double只能用来做科学计算或者是工程计算,在商业计算中还是要用java.math.BigDecimal。
public class BigDecimal extends Number implements Comparable<BigDecimal> { ... }
源码比较多没有全贴出了,但是从类名上可以看到 BigDecimal extends Number 万变不离其宗呢这是。
BigDecimal有4个构造方法:
BigDecimal(int) 创建一个具有参数所指定整数值的对象。
BigDecimal(double) 创建一个具有参数所指定双精度值的对象。(不建议采用)
BigDecimal(long) 创建一个具有参数所指定长整数值的对象。
BigDecimal(String) 创建一个具有参数所指定以字符串表示的数值的对象
官方文档:https://docs.oracle.com/javase/6/docs/api/java/math/BigDecimal.html
代码分析
在正式对BigDecimal分析之前先看一段代码
public static void main(String[] args) {
System.out.println(0.2 + 0.1);
System.out.println(0.3 - 0.1);
System.out.println(0.4 * 0.1);
System.out.println(0.6 / 0.1);
System.out.println(0.6 / 0.2);
System.out.println(0.7 / 0.1);
System.out.println(0.7 / 0.2);
}
输出结果
0.30000000000000004
0.19999999999999998
0.04000000000000001
5.999999999999999
2.9999999999999996
6.999999999999999
3.4999999999999996
为什么会出现这种情况?什么原因导致的呢?
是因为我们的计算机是二进制的。浮点数没有办法是用二进制进行精确表示。我们的CPU表示浮点数由两个部分组成:指数和尾数,这样的表示方法一般都会失去一定的精确度,有些浮点数运算也会产生一定的误差。如:3.5的二进制表示并非就是精确的3.5。反而最为接近的二进制表示是3.4999999999999996。浮点数的值实际上是由一个特定的数学公式计算得到的。
看下BigDecimal的用法
public static void main(String[] args) {
BigDecimal bigInt = new BigDecimal(1);
BigDecimal bigDouble = new BigDecimal(0.1);
BigDecimal bigString = new BigDecimal("0.1");
System.out.println("bigInt = " + bigInt);
System.out.println("bigDouble = " + bigDouble);
System.out.println("bigString = " + bigString);
}
输出结果
bigInt = 1
bigDouble = 0.1000000000000000055511151231257827021181583404541015625
bigString = 0.1
会发现bigDouble的结果是这样的。为什么呢?
原因啊在《public BigDecimal(double val) {...}》这个构造方法上已经解释了。这个构造函数的结果可能有些不可预测,有人可能会认为,将@code new bigdecimal(0.1)写入Java创建了一个{代码BigDeMix},它完全等于0.1(非比例值为1,比例为1),但它是实际上等于10000000000000055511151231257827021181583404541015625。这是因为0.1不能精确表示为。这是因为0.1无法准确地表示为 double(或者说对于该情况,不能表示为任何有限长度的二进制小数)。这样,传入到构造方法的值不会正好等于 0.1(虽然表面上等于该值)另一方面,String 构造方法是完全可预知的:写入 newBigDecimal("0.1") 将创建一个 BigDecimal,它正好等于预期的 0.1。因此,比较而言,通常建议优先使用String构造方法。还说了如果想用这个方法,double必须用作BigDecimal的源时,请使用Double.toString(double)转成String,然后使用String构造方法,或使用BigDecimal的静态方法valueOf,如下
public static void main(String[] args) {
BigDecimal bigDouble = new BigDecimal(0.1);
System.out.println("bigDouble = " + bigDouble);
System.out.println("两种方法转换后");
BigDecimal bigDouble1 = BigDecimal.valueOf(0.1);
BigDecimal bigDouble2 = new BigDecimal(Double.toString(0.1));
System.out.println("bigDouble1 = " + bigDouble1);
System.out.println("bigDouble2 = " + bigDouble2);
}
输出结果
bigDouble = 0.1000000000000000055511151231257827021181583404541015625
两种方法转换后
bigDouble1 = 0.1
bigDouble2 = 0.1
这样就可以了。
在看下BigDecimal的加减乘除,这可不是运算符的(+ - * /)了,而是提供了方法的。
public BigDecimal add(BigDecimal value); //加法
public BigDecimal subtract(BigDecimal value); //减法
public BigDecimal multiply(BigDecimal value); //乘法
public BigDecimal divide(BigDecimal value); //除法
===========用一下呗
public static void main(String[] args) {
BigDecimal b1 = new BigDecimal("3.3");
BigDecimal b2 = new BigDecimal("2.2");
System.out.println("b1 + b2 = " + b1.add(b2));
System.out.println("b1 - b2 = " + b1.subtract(b2));
System.out.println("b1 * b2 = " + b1.multiply(b2));
System.out.println("b1 / b2 = " + b1.divide(b2));
}
输出结果
b1 + b2 = 5.5
b1 - b2 = 1.1
b1 * b2 = 7.26
b1 / b2 = 1.5
注意
除法divide在不能整除的情况下会抛出一个异常:
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
at java.math.BigDecimal.divide(BigDecimal.java:1690)
其实divide方法有可以传三个参数:
public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)
第一参数表示除数, 第二个参数表示小数点后保留位数,
第三个参数表示舍入模式,只有在作除法运算或四舍五入时才用到舍入模式,有下面这几种
ROUND_CEILING //向上取整
ROUND_DOWN //向零方向舍入
ROUND_FLOOR //向下取整
ROUND_HALF_DOWN //向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,向下舍入, 例如1.55 保留一位小数结果为1.5
ROUND_HALF_EVEN //向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,如果保留位数是奇数,使用ROUND_HALF_UP,如果是偶数,使用ROUND_HALF_DOWN
ROUND_HALF_UP //向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,向上舍入, 1.55保留一位小数结果为1.6
ROUND_UNNECESSARY //计算结果是精确的,不需要舍入模式
ROUND_UP //向远离0的方向舍入
都试一下看看会怎样
public static void main(String[] args) {
BigDecimal b1 = new BigDecimal("3.3");
BigDecimal b2 = new BigDecimal("1.9");
System.out.println("b1 / b2 = ROUND_CEILING " + b1.divide(b2,2,BigDecimal.ROUND_CEILING));
System.out.println("b1 / b2 = ROUND_DOWN " + b1.divide(b2,2,BigDecimal.ROUND_DOWN));
System.out.println("b1 / b2 = ROUND_FLOOR " + b1.divide(b2,2,BigDecimal.ROUND_FLOOR));
System.out.println("b1 / b2 = ROUND_HALF_DOWN " + b1.divide(b2,2,BigDecimal.ROUND_HALF_DOWN));
System.out.println("b1 / b2 = ROUND_HALF_EVEN " + b1.divide(b2,2,BigDecimal.ROUND_HALF_EVEN));
System.out.println("b1 / b2 = ROUND_HALF_UP " + b1.divide(b2,2,BigDecimal.ROUND_HALF_UP));
// System.out.println("b1 / b2 = ROUND_UNNECESSARY " + b1.divide(b2,2,BigDecimal.ROUND_UNNECESSARY));计算结果是精确的,不需要舍入
System.out.println("b1 / b2 = ROUND_UP " + b1.divide(b2,2,BigDecimal.ROUND_UP));
}
输出结果
b1 / b2 = ROUND_CEILING 1.74
b1 / b2 = ROUND_DOWN 1.73
b1 / b2 = ROUND_FLOOR 1.73
b1 / b2 = ROUND_HALF_DOWN 1.74
b1 / b2 = ROUND_HALF_EVEN 1.74
b1 / b2 = ROUND_HALF_UP 1.74
b1 / b2 = ROUND_UP 1.74
基本上这些都是会在工作中比较常用的类。