深入解析基本类型

基本类型的简介

基本类型的两条准则:

  • Java中,如果对整数不指定类型,默认时int类型,对小数不指定类型,默认是double类型。

  • 基本类型由小到大,可以自动转换,但是由大到小,则需要强制类型转换。

  • Java中数据类型主要分为两大类:基本数据类型和引用数据类型。 基本数据类型共有8种,分别是:布尔型boolean, 字符型char和数值型byte/short/int/long/float/double。由于字符型char所表示的单个字符与Ascii码中相应整形对应,因此,有时也将其划分到数值型中。引用类型具体可分为:数组、类和接口。因此,本文中Java类型转换的总结也将分为基本数据类型和引用数据类型两个方面展开。

  • 基本数据类型中,布尔类型boolean占有一个字节,由于其本身所代码的特殊含义,boolean类型与其他基本类型不能进行类型的转换(既不能进行自动类型的提升,也不能强制类型转换), 否则,将编译出错。

  • 所占的字节数:

byte: 1个字节;
char: 2个字节;
short: 2个字节;
int: 4个字节;
long: 8个字节;
float: 4个字节;(6位小数,指数是:10^-38~10^38; 范围:)
double: 8个字节;

基本类型的后缀:

long : l 或 L
float: f 或 F;
double: d 或 D

基本数据类型的类型转换

基本数据类型中,布尔类型boolean占有一个字节,由于其本身所代码的特殊含义,boolean类型与其他基本类型不能进行类型的转换(既不能进行自动类型的提升,也不能强制类型转换), 否则,将编译出错。

基本数据类型中数值类型的自动类型提升

数值类型在内存中直接存储其本身的值,对于不同的数值类型,内存中会分配相应的大小去存储。如:byte类型的变量占用8位,int类型变量占用32位等。相应的,不同的数值类型会有与其存储空间相匹配的取值范围。具体如下所示:

图中依次表示了各数值类型的字节数和相应的取值范围。在Java中,整数类型(byte/short/int/long)中,对于未声明数据类型的整形,其默认类型为int型。在浮点类型(float/double)中,对于未声明数据类型的浮点型,默认为double型。

package com.corn.testcast;


public class TestCast {
    
    public static void main(String[] args) {
        byte a = 1000;   // 编译出错 Type mismatch: cannot convert from int to byte
        float b = 1.5;   // 编译出错 Type mismatch: cannot convert from double to float
        byte c = 3;      // 编译正确
    }
    
}

是不是有点奇怪?按照上面的思路去理解,将一个int型的1000赋给一个byte型的变量a,编译出错,提示"cannot convert from int to byte"是对的,1.5默认是一个double型,将一个double类型的值赋给一个float类型,编译出错,这也是对的。但是最后一句:将一个int型的3赋给一个byte型的变量c,居然编译正确,这是为什么呢?

原因在于:jvm在编译过程中,对于默认为int类型的数值时,当赋给一个比int型数值范围小的数值类型变量(在此统一称为数值类型k,k可以是byte/char/short类型),会进行判断,如果此int型数值超过数值类型k,那么会直接编译出错。因为你将一个超过了范围的数值赋给类型为k的变量,k装不下嘛,你有没有进行强制类型转换,当然报错了。但是如果此int型数值尚在数值类型k范围内,jvm会自定进行一次隐式类型转换,将此int型数值转换成类型k。如图中的虚线箭头。这一点有点特别,需要稍微注意下。

package com.corn.testcast;

public class TestCast {

    public static void main(String[] args) {
        long a = 10000000000; //编译出错: The literal 10000000000 of type int is out of range 
        long b = 10000000000L; //编译正确
        int c = 1000;
        long d = c;
        float e = 1.5F;
        double f = e;
    }

}

如上:定义long类型的a变量时,将编译出错,原因在于10000000000默认是int类型,同时int类型的数值范围是-2^31 ~ 2^31-1,因此,10000000000已经超过此范围内的最大值,故而其自身已经编译出错,更谈不上赋值给long型变量a了。

此时,若想正确赋值,改变10000000000自身默认的类型即可,直接改成10000000000L即可将其自身类型定义为long型。此时再赋值编译正确。

将值为1000的int型变量c赋值给long型变量d,按照上文所述,此时直接发生了自动类型提升, 编译正确。同理,将e赋给f编译正确。

**接下来,还有一个地方需要注意的是:char型其本身是unsigned型,同时具有两个字节,其数值范围是0 ~ 2^16-1,因为,这直接导致byte型不能自动类型提升到char,char和short直接也不会发生自动类型提升(因为负数的问题),同时,byte当然可以直接提升到short型。 **

基本数据类型中的数值类型强制转换

当我们需要将数值范围较大的数值类型赋给数值范围较小的数值类型变量时,由于此时可能会丢失精度(1讲到的从int到k型的隐式转换除外),因此,需要人为进行转换。我们称之为强制类型转换。

package com.corn.testcast;

public class TestCast {

    public static void main(String[] args) {
        byte p = 3; // 编译正确:int到byte编译过程中发生隐式类型转换
        int  a = 3;
        byte b = a; // 编译出错:cannot convert from int to byte
        byte c = (byte) a; // 编译正确
        float d = (float) 4.0;
    }

}

byte p =3;编译正确在1中已经进行了解释。接下来将一个值为3的int型变量a赋值给byte型变量b,发生编译错误。这两种写法之间有什么区别呢?

区别在于前者3是直接量,编译期间可以直接进行判定,后者a为一变量,需要到运行期间才能确定,也就是说,编译期间为以防万一,当然不可能编译通过了。此时,需要进行强制类型转换。

强制类型转换所带来的结果是可能会丢失精度,如果此数值尚在范围较小的类型数值范围内,对于整型变量精度不变,但如果超出范围较小的类型数值范围内,很可能出现一些意外情况。

package com.corn.testcast;

public class TestCast {

    public static void main(String[] args) {
        int a = 233;
        byte b = (byte) a;
        System.out.println("b:" + b);  // 输出:-23
    }

}

为什么结果是-23?需要从最根本的二进制存储考虑。

233的二进制表示为:24位0 + 11101001,byte型只有8位,于是从高位开始舍弃,截断后剩下:11101001,由于二进制最高位1表示负数,0表示正数,其相应的负数为-23。

进行数学运算时的数据类型自动提升与可能需要的强制类型转换
package com.corn.testcast;

public class TestCast {

    public static void main(String[] args) {
        byte a = 3 + 5; // 编译正常 编译成 3+5直接变为8
        int b = 3, c = 5;
        byte d = b + c; // 编译错误:cannot convert from int to byte

        byte e = 10, f = 11;
        byte g = e + f; // 编译错误 +直接将10和11类型提升为了int
        byte h = (byte) (e + f);  //编译正确
    }

}

正如前面所说的,类型由大到小,是必需强制转换。但这并不意味着需要用户手动强制转换 —— 也就是 隐式转换。隐式转换 说的透彻点就是由编译器来进行强制转换,不需要用户再去写强制转换的代码。下面的前两个小点所说的便是特殊的隐式类型转换。

本小节所讨论的类型转换是不包括 类型由小到大的转换,讨论的是其他比较容易让人迷惑的类型转换

int类型的字面常量转换成比int类型低的变量类型
/**
 * @program: demo
 * @description: java基础知识测试
 * @author: lee
 * @create: 2018-07-18 09:45
 **/
public class Demo {

    public static void main(String[] args) {
      int a=9;
      byte b=9;
      char c=9+5;
      short s=(short) (c+10);
    }
}

上面的代码是经过编译的,是正确的。b是byte类型,但b=9不需要显式地手动强制转换,这是因为9是字面常量,是由JVM自动完成。   我们再来看一下c=9+5,c是char类型,9+5得到结果是int类型,但也不需要显式地手动强制转换。这是因为 9+5是常量表达式,所以在编译期间已经由编译器计算出结果了,即经过编译后,相当于 c=14,也是字面常量,所以可以隐式转换。同理,short s = (short) (c+10); 子所以不能隐式转换,就是因为表达式不是常量表达式,包含了变量,只能在运行期间完成,所以就要手动强制转换。

整形字面常量隐式转换的限制:

整形字面常量的大小超出目标类型所能表示的范围时,要手动强制类型转换。

byte b = 128;//编译错误,128超出byte类型所能表示的范围
byte c = (byte)128;//编译通过

对于传参数时,必须要显式地进行强制类型转换,明确转换的类型,编译器子所以这样要求,其实为了避免 方法重载出现的隐式转换 与 小类型自动转大类型 发生冲突。

public static void main(String[] args) {
    shortMethod(8);//编译错误
    shortMethod((short)8); //编译通过
    longMethod(8);//编译通过,因为这是小类型变成大类型,是不需要强制类型转换的
}
public static void shortMethod(short c){
    System.out.println(c);
}
public static void longMethod(long l){
    System.out.println(l);
}
强制类型转换数据溢出的结果计算

我们知道计算机中的数据以字节的形式存储,一个字节又分出8位地址空间,用来存储1和0二进制数据。

Java基础类型强转换:溢出部分丢去,保留底位有效,以1 byte(1字节)为例,有效为后7位,高位为符号位。

// 格式:数值(进制),如258(10)是指十进制的258.
 
int i=258;      // 258(10)=0000 0000 0000 0000 0000 0001 0000 0010(2)
byte b=(byte)i; // 取0000 0010(2)溢出部分丢去,左高位是0代表正数,后7位是000 0010代表2(10),
 
System.out.print(b); // 所以,打印2

Java 原码、反码、补码详解

机器数和真值

一个数在计算机中的二进制表示形式, 叫做这个数的机器数。机器数是带符号的,在计算机用一个数的最高位存放符号, 正数为0, 负数为1.

比如,十进制中的数 +3 ,计算机字长为8位,转换成二进制就是00000011。如果是 -3 ,就是 10000011 。

那么,这里的 00000011 和 10000011 就是机器数。

真值

因为第一位是符号位,所以机器数的形式值就不等于真正的数值。例如上面的有符号数 10000011,其最高位1代表负,其真正数值是 -3 而不是形式值131(10000011转换成十进制等于131)。所以,为区别起见,将带符号位的机器数对应的真正数值称为机器数的真值。

例:0000 0001的真值 = +000 0001 = +1,1000 0001的真值 = –000 0001 = –1
原码, 反码, 补码的基础概念和计算方法.

原码

原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值

比如如果是8位二进制:

[+1]原 = 0000 0001

[-1]原 = 1000 0001

第一位是符号位. 因为第一位是符号位, 所以8位二进制数的取值范围就是:

[1111 1111 , 0111 1111] 即 [-127 , 127]

反码

反码的表示方法是:

正数的反码是其本身

负数的反码是在其原码的基础上, 符号位不变,其余各个位取反.
[+1] = [00000001]原 = [00000001]反

[-1] = [10000001]原 = [11111110]反

可见如果一个反码表示的是负数, 人脑无法直观的看出来它的数值. 通常要将其转换成原码再计算.

补码

补码的表示方法是:

正数的补码就是其本身

负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)

[+1] = [00000001]原 = [00000001]反 = [00000001]补

[-1] = [10000001]原 = [11111110]反 = [11111111]补
为何要使用原码, 反码和补码

现在我们知道了计算机可以有三种编码方式表示一个数. 对于正数因为三种编码方式的结果都相同:

[+1] = [00000001]原 = [00000001]反 = [00000001]补

所以不需要过多解释. 但是对于负数:

[-1] = [10000001]原 = [11111110]反 = [11111111]补

可见原码, 反码和补码是完全不同的. 既然原码才是被人脑直接识别并用于计算表示方式, 为何还会有反码和补码呢?

首先, 因为人脑可以知道第一位是符号位, 在计算的时候我们会根据符号位, 选择对真值区域的加减. (真值的概念在本文最开头). 但是对于计算机, 加减乘数已经是最基础的运算, 要设计的尽量简单. 计算机辨别"符号位"显然会让计算机的基础电路设计变得十分复杂! 于是人们想出了将符号位也参与运算的方法. 我们知道, 根据运算法则减去一个正数等于加上一个负数, 即: 1-1 = 1 + (-1) = 0 , 所以机器可以只有加法而没有减法, 这样计算机运算的设计就更简单了.

于是人们开始探索 将符号位参与运算, 并且只保留加法的方法. 首先来看原码:

计算十进制的表达式: 1-1=0

1 - 1 = 1 + (-1) = [00000001]原 + [10000001]原 = [10000010]原 = -2

如果用原码表示, 让符号位也参与计算, 显然对于减法来说, 结果是不正确的.这也就是为何计算机内部不使用原码表示一个数.

为了解决原码做减法的问题, 出现了反码:

1 - 1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原= [0000 0001]反 + [1111 1110]反 = [1111 1111]反 = [1000 0000]原 = -0

发现用反码计算减法, 结果的真值部分是正确的. 而唯一的问题其实就出现在"0"这个特殊的数值上. 虽然人们理解上+0和-0是一样的, 但是0带符号是没有任何意义的. 而且会有[0000 0000]原和[1000 0000]原两个编码表示0.

于是补码的出现, 解决了0的符号以及两个编码的问题:

1-1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原 = [0000 0001]补 + [1111 1111]补 = [0000 0000]补=[0000 0000]原

这样0用[0000 0000]表示, 而以前出现问题的-0则不存在了.而且可以用[1000 0000]表示-128:

(-1) + (-127) = [1000 0001]原 + [1111 1111]原 = [1111 1111]补 + [1000 0001]补 = [1000 0000]补

-1-127的结果应该是-128, 在用补码运算的结果中, [1000 0000]补 就是-128. 但是注意因为实际上是使用以前的-0的补码来表示-128, 所以-128并没有原码和反码表示.(对-128的补码表示[1000 0000]补算出来的原码是[0000 0000]原, 这是不正确的)

使用补码, 不仅仅修复了0的符号以及存在两个编码的问题, 而且还能够多表示一个最低数. 这就是为什么8位二进制, 使用原码或反码表示的范围为[-127, +127], 而使用补码表示的范围为[-128, 127].

因为机器使用补码, 所以对于编程中常用到的32位int类型, 可以表示范围是: [-231, 231-1] 因为第一位表示的是符号位.而使用补码表示时又可以多保存一个最小值.

复合运算符的隐式转换

复合运算符(+=、-=、*=、/=、%=)是可以将右边表达式的类型自动强制转换成左边的类型

public static void main(String[] args) {
    int a = 8;  
    short s = 5; 
    s += a;
    s += a+5;   
}

s+=a、s+=a+5;的表达式计算结果都是int类型,但都不需要手动强制转换。其实,如果是反编译这段代码的class文件,你会发现s+=a;,其实是被编译器处理成了

s=(short)(s+a)

也就是说对于所有的复合运算的隐式类型转换,其实是编译器自动添加类型转换的代码。 所以,相对于整形字面常量的隐式转换,复合运算符的隐式转换则没有任何限制因为前者只能在编译器期间发生,后者则是编译器实实在在的补全了类型转换的代码。

特殊的char类型

char类型在基本类中是一个比较特殊的存在。这种特殊性在于char类型是一个无符号类型,所以char类型与其他基本类型不是子集与父集间的关系(其他类型都是有符号的类型)。也就是说,char类型与byte、short之间的转换都需要显式的强制类型转换(小类型自动转换成大类型失败)。

同时,由于char类型是一个无符号类型,所以对于整形字面常量的隐式转换的限制,不仅包括字面常量数值的大小不能超出2个字节,还包括字面常量数值不能为负数

 byte b = 2;
 char c = 2;//编译通过
      c = 100000000000;//编译不通过,超出char类型的范围
 char d = -2//字面常量为负数,编译不通过
      d = (char)-100;//编译通过
 char f = (char)b; //编译通过,必须显式的强制类型转换
      f = b;//编译不通过,不能隐式转换
 int  i = c;//编译通过,可以不需要强制类型转换
 short s = (short) c;//编译通过,必须显式地强制类型转换

 char类型是无符号的类型,这种无符号也体现在在其转换成int类型时,也就是说,char类型在扩展时,也是按无符号的方式扩展,扩展位填0。我们来看一个例子:

 public static void main(String[] args) {
    short s = -5;
    char c = (char)s; 
    System.out.println(c==s);  //false
    System.out.println("(int)c = "+(int)c); //转换成int类型,值为65531
    System.out.println("(short)c = "+(short)c); //-5
    System.out.println("(int)s = "+(int)s);//-5
}

从上面的结果发现,char类型的c 与 short类s其实存储字节码内容是一样的,但由于前者是无符号,所以扩展成int类型的结果是 65531,而不是 -5。运算符==比较的就是他们扩展成int类型的值,所以为fasle。

对char类型的类型转换,可以总结成以下几点:

char类型与byte、short的相互转换,都需要显式地强类型制转换。
 
对于数值是负数的,都需要进行显式地强制类型转换,特别是在整形字面常量的隐式转换中。

char类型转换成int、long类型是符合 小类型转大类型的规则,即无需要强制类型转换。

运算结果的类型

在Java中,一个运算结果的类型是与表达式中类型最高的相等,如:

char cc = 5;
float dd = 0.6f+cc;//最高类型是float,运算结果是float
float ee = (float) (0.6d+cc);//最高类型是double,运算结果也是double
int aa = 5+cc;//最高类型是int,运算结果也为int

 但是,对于最高类型是byte、short、char的运算来说,则运行结果却不是最高类型,而是int类型。看下面的例子,c、d运算的最高类型都是char,但运算结果却是int,所以需要强制类型转换。

byte b = 2;
char a = 5;
char c = (char) (a+b);//byte+char,运算结果的类型为int,需要强制类型转换
int  e = a+b;//编译通过,不需要强制类型转换,可以证明是int
char d = (char) (a+c);//char+char,
short s1 = 5;
short s2 = 6;
short s3 =(short) (s1+s2);

综上所述,java的运算结果的类型有两个性质:

运算结果的类型必须是int类型或int类型以上。

最高类型低于int类型的,运算结果都为int类型。否则,运算结果与表达式中最高类型一致

浮点数类型

我们都知道,long类型转换成float类型是不需要强制类型转换的,也就是说相对于flaot类型,long类型是小类型,存储的范围要更小。然而flaot只占了4个字节,而long却占了8个字节,long类型的存储空间要比float类型大。这究竟是怎么一回事,我们接下来将细细分析。

浮点数使用 IEEE(电气和电子工程师协会)格式。 浮点数类型使用 符号位、指数、有效位数(尾数)来表示。要注意一下,尾数的最高

在java中,float 和 double 的结构如下:

类型|符号位|指数域|有效位域 -|-|- float|1位|8位|23位 double|1位|11位|52位 符号位: 0为正,1为负;

指数域: 无符号的,float的偏移量为127(即float的指数范围是-126~127,),double 有效位域: 无符号的

浮点类型的两个需要注意的地方

存储的小数的数值可能是模糊值

public static void main(String[] args) {
    double d1 = 0.1;
    double d2 = 0.2;
    System.out.println(d1+d2 == 0.3);
    System.out.println(d1+d2);
}
false
0.30000000000000004

上述的运算结果并不是错误。这是因为无法用二进制来准确地存储的0.3,这是一个无限循环的值,与10进制的1/3很相似。不只是0.3,很多小数都是无法准确地用浮点型表示,其实这是由 小数的十进制转成二进制的算法所决定的,十进制的小数要不断乘2,直到最后的结果为整数才是最后的二进制值,但这有可能怎么也得不到整数,所以最后得到的结果可能是一个 无限值 ,浮点型就无法表示了

但是对于 整数 来说,在浮点数的有效范围内,则都是精确的。同样,也是由于转换算法:十进制的整数转成二进制的算法是不断对2求余数,所以 不会存在无限值的情况;

浮点数的有效位及精度

浮点型所能表示的有效位是有限的,所以哪怕是整数,只要超出有效位数,也只能存储相似值,也就是该数值的最低有效位将会丢失,从而造精度丢失。

float类型的二进制有效位是24位,对应十进制的7 ~ 8位数字;double类型的二进制53位,对应十进制的10 ~ 11位数字。

double、float类型 所能表示的范围比int、long类型表示的范围要广,也浮点类型属于大类型。但是,并不能完美地表整形,浮点类型的精度丢失会造成一些问题。

 int a = 3000000;
        int b = 30000000;
        float f1 = a;
        float f2 = b;
        System.out.println("3000000==3000001 "+(f1==f1+1));
        System.out.println("30000000==30000001 "+(f2==f2+1));
        System.out.println("3000000的有效二进制位数:"+ Integer.toBinaryString(a).length());
        System.out.println("30000000的有效二进制位数:"+ Integer.toBinaryString(b).length());
3000000 == 3000001  false
30000000 == 30000001  true
3000000的有效二进制位数: 22
30000000的有效二进制位数: 25
解决浮点型精度丢失的问题

 浮点型带来精度丢失的问题是很让人头痛的,所以一般情况下,在程序中是不会使用float、double来存储比较大的数据。而商业计算往往要求结果精确。《Effactive Java》书中有一句话:

float和double类型的主要设计目标是为了科学计算和工程计算

首先我们要搞清楚下面两个问题:
 
     (1) 十进制整数如何转化为二进制数
 
           算法很简单。举个例子,11表示成二进制数:
 
                     11/2=5 余   1
 
                       5/2=2   余   1
 
                       2/2=1   余   0
 
                       1/2=0   余   1
 
                          0结束         11二进制表示为(从下往上):1011
 
          这里提一点:只要遇到除以后的结果为0了就结束了,大家想一想,所有的整数除以2是不是一定能够最终得到0。换句话说,所有的整数转变为二进制数的算法会不会无限循环下去呢?绝对不会,整数永远可以用二进制精确表示 ,但小数就不一定了。
 
      (2) 十进制小数如何转化为二进制数
 
           算法是乘以2直到没有了小数为止。举个例子,0.9表示成二进制数
 
                     0.9*2=1.8   取整数部分 1
 
                     0.8(1.8的小数部分)*2=1.6    取整数部分 1
 
                     0.6*2=1.2   取整数部分 1
 
                     0.2*2=0.4   取整数部分 0
 
                     0.4*2=0.8   取整数部分 0
 
                     0.8*2=1.6 取整数部分 1
 
                     0.6*2=1.2   取整数部分 0
 
                              .........      0.9二进制表示为(从上往下): 1100100100100......
 
           注意:上面的计算过程循环了,也就是说*2永远不可能消灭小数部分,这样算法将无限下去。很显然,小数的二进制表示有时是不可能精确的 。其实道理很简单,十进制系统中能不能准确表示出1/3呢?同样二进制系统也无法准确表示1/10。这也就解释了为什么浮点型减法出现了"减不尽"的精度丢失问题。

这个思路很正确,但是如果你仔细看看API里关于BigDecimal的详细说明,你就会知道,如果需要精确计算,我们不能直接用double,而非要用 String来构造BigDecimal不可!所以,我们又开始关心BigDecimal类的另一个方法,即能够帮助我们正确完成精确计算的 BigDecimal(String value)方法。

package com.util;
import java.math.BigDecimal;
 
/**
 * 由于Java的简单类型不能够精确的对浮点数进行运算,这个工具类提供精确的浮点数运算,包括加減乘除和四捨五入。
 */
public class Arith {
 
    //默认吃吃饭运算精度
    private static final int DEF_DIV_SCALE = 10;
 
    //这个类不能实例化
    private Arith() {
         
    }
 
    /**
     * 提供精确的加法运算
     *
     * @param v1
     *            被加数
     * @param v2
     *            加数
     * @return 两个参数的和
     */
    public static double add(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.add(b2).doubleValue();
    }
 
    /**
     * 提供精确的减法运算
     * @param v1
     *            被減数
     * @param v2
     *            減数
     * @return两个参数的差
     */
    public static double sub(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.subtract(b2).doubleValue();
    }
 
    /**
     * 提供精确的乘法运算
     *
     * @param v1
     *            被乘数
     * @param v2
     *            乘数
     * @return 两个参数的积
     */
    public static double mul(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.multiply(b2).doubleValue();
    }
 
    /**
     * 提供(相对)精确的除非运算,当发生除不尽的情况时,精确到小数点以后10位,以后的数字四舍五入
     * @param v1
     *            被除數
     * @param v2
     *            除數
     * @return 兩個參數的商
     */
    public static double div(double v1, double v2) {
        return div(v1, v2, DEF_DIV_SCALE);
    }
 
    /**
     * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指定精度,以后的数字四舍五入
     * @param v1
     *            被除數
     * @param v2
     *            除數
     * @param scale
     *            表示表示需要精確到小數點以後位数。
     * @return 兩個參數的商
     */
    public static double div(double v1, double v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
    }
 
    /**
     * 提供精確的小數位四捨五入處理。
     * 提供精确的小数位四舍五入处理
     *
     * @param v
     *            需要四捨五入的數位
     * @param scale
     *            小數點後保留幾位
     * @return 四捨五入後的結果
     */
    public static double round(double v, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b = new BigDecimal(Double.toString(v));
        BigDecimal one = new BigDecimal("1");
        return b.divide(one, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
    }
}

转载于:https://my.oschina.net/jiansin/blog/1865185

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值