Java整型, 浮点型数值的运算

发布时间 2023-04-16 17:02:06作者: 为了生活更好

 

 

二进制 binary

八进制 octal

十进制 decimal

十六进制 hex

 

 

 

基本数据类型

boolean 1bit

byte 1byte(8bit) -2^(8-1)~2^(8-1)-1  -128~127

short 2byte -2^15~2^15-1 -32768-32767

char 2byte  在Java中最大值为'\uFFFF'

int 4byte  -2,147,483,648~2,147,483,647 约21亿

long 8byte -9,223,372,036,854,775,808~9,22 3,372, 036,8 54,77 5,807 约922 亿亿

 

原码反码补码的数学定义:

若x使用n位二进制存储

下面举例都以n为8说明

原码

2^(n-1)>x>=0时原码为x

-2^(n-1)<x<=0时原码为2^(n-1)-x

例如8原码为 0000 1000

-8原码为 1000 1000  (128-(-8)) 如果看做无符号数是 136

 

反"码

2^(n-1)>x>=0时反码为x

-2^(n-1)<x<=0时反码为2^n - 1 + x  这里2^n也称为模

例如8的反码为0000 1000

-8的反码为  1111 0111 (256-1+(-8)) 如果看做无符号数是 247

 

补码

2^(n-1)>x>=0时 补码为x

-2^(n-1)<x<=0时补码为2^n+x

例如8的补码为 0000 1000

-8的补码为 1111 1000 *(256+(-8)) 如果把其对应的二进制序列看做无符号数是248

 

使用补码的原因:

    计算机中做求和运算时使用的加法器, 从真值表可以看到其本位和是异或的结果, 进位是与的结果

    使用补码后, 本来要做减法的现在可以用补码的加法来计算

例如10-8=2

被转换为了10 +   2^8 + (-8) =2^8 + 2, 而补码运算对符号位运算产生的进位要舍弃, 也就相当于2^8 +2 - 2^8

 

上面是正常舍去进位的情况, 有时候是数值溢出了

例如8位二进制补码最多表示-128-127

如果两个127相加

结果254, 实际二进制是11111110 从符号位就可以看到这个数表示的是个负数

-2, Java在算术运算时如果数据溢出了不会抛异常或者Error, 而是会直接截断

怎么判断是正常舍入还是数值溢出呢?

可以通过java.lang.math里的addExact和subtractExtract来判断是否结果溢出, 如果溢出了会抛出异常

其原理是根据符号位的变化

 

 

十进制和二进制转换

对整数 除二取余 , 最后获得的余数放在最高位(左端)

对小数 乘二取整, 第一个获得的整数放在最靠近小数点位置

 

二进制与其它进制转换

每3位二进制对应一个八进制数字

每4位二进制对应一个十六进制数字

 

例如转换为16进制时, 对整数部分高位补0到长度是4的整数倍, 对小数部分低位补0到长度是4的整数倍, 然后直接转换

 

通过取反等操作获取整数的补码

原码: 符号位+数值部分 0为正 1为负

反码: 对正数, 反码与原码相同, 对负数, 反码是符号位之外的取反

补码: 对正数, 补码与原码相同, 对负数, 是反码+1

数值部分计算: 除2取余

8/2 = 4余0

4/2= 2余0

2/2 = 1余0

1/2 = 0余1

此时8的数值部分就是1000

假如按单字节存储的话, 那么原码反码补码列举如下

8的原码 0000 1000

8的反码 0000 1000

8的补码 0000 1000

 

-8的原码 1000 1000

-8的反码 1111 0111

-8的补码 1111 1000

 

负数的补码转换为原码, 按位取反+1

1000 0000

按位取反

1111 1111

+1

(进位1存不下被丢弃) 0000 0000

这种-0 规定为-128

 

当n为4byte也就是32bit时

8的补码0000 0000    0000 0000    0000 0000 0000 1000 (二进制表示)

也就是00 00 00 10(16进制表示)

-8的原码 10000000 00000000 00000000 00001000

-8的反码 11111111 11111111 11111111 11110111

-8的补码 11111111 11111111 11111111 11111000

补码的16进制为FF FF FF F8

 

验证:

int a=-8;

String str = Integer.toHexString(a);

System.out.println(str);//输出结果为fffffff8

 

 

float 4byte

浮点数在Java中以尾数形式存储

 1bit(符号位) 8bits(指数位) 23bits(尾数位)

符号位表示 数值的正负

符号位+1.尾数*10^(尾数位-127) 为浮点数的真实值

 

十进制小数转换为尾数的计算方式

乘2取整

 

可以计算0.5的二进制表示

0.5 * 2 = 1.0 整数部分1

于是0.5的二进制

0.1

使用尾数表示法

1*2^-1

在java中隐含了这个1

因此0(符号位)   -1+127(指数位) 000 0000 0000 0000 0000 0000(尾数位)

得到0  01111110 000 0000 0000 0000 0000 0000

也就是16进制

3F000000

 

验证代码:

float f = 0.5f;

int binary = Float.floatToIntBits(f);

System.out.println(Integer.toHexString(binary));//输出3f000000

 

 

但是也可能出现循环的情况

以0.2为例

0.2 * 2 = 0.4  整数部分0

0.4* 2 = 0.8 整数部分0

0.8*2 = 0.6 +1整数部分1

0.6*2 = 1.2 整数部分1

0.2*2= 0.4 整数部分0

发现出现了循环

于是0.0011 0011 001....

因此浮点数的0.2如果以尾数表示法直接存储时存储的不是准确值

 

此时使用尾数存储为

1.1 0011 001...........*2^(-3)

 

0(符号位) 127+(-3)(指数位) + 10011001100110011001100

0 01111100 10011001100110011001100 (1)

3E4CCCCC

 

代码验证:

float f1 = 0.2f;

int binary1 = Float.floatToIntBits(f1);

System.out.println(Integer.toHexString(binary1));//3e4ccccd 这是因为存储不下时发生了舍入

 

String hexString = "3E4CCCCC"; // 十六进制字符串

int intValue = Integer.parseInt(hexString, 16); // 将十六进制字符串转换为整数值

float floatValue = Float.intBitsToFloat(intValue); // 将整数值转换为float类型变量

System.out.println("Hex String: " + hexString);

System.out.println("Float Value: " + floatValue);

 

使用Jclasslib byteviewer查看常量池, 发现在编译时0.2就被存储为3e4ccccd

 

 

浮点型表示范围

从尾数表示法可以推出其表示的最大数位

0 255-127 111 1111 1111 1111 1111 1111

也就是

1.111 1111 1111 1111 1111 1111*2^128  约为3.403E38 ~ 3.403E38

 

浮点数精度极限为

推测

1.000 0000 0000 0000 0000 0000*2^-127

约为5.87747175411E-39

实际测试验证发现

String hexString = "00000001"; // 十六进制字符串

        int intValue = Integer.parseInt(hexString, 16); // 将十六进制字符串转换为整数值

        float floatValue = Float.intBitsToFloat(intValue); // 将整数值转换为float类型变量

        System.out.println("Hex String: " + hexString);

        System.out.println("Float Value: " + floatValue);

Hex String: 00000001

Float Value: 1.4E-45 约为2的-150次幂 怀疑是Java对齐做了优化 实际上2^-150约为1.576*10^-45次幂

 

精度和范围不能兼得

 

double 8byte

指数段有11位

尾数段有52位

1.798E308 ~ 1.798E308

 

 

自动类型转换

boolean 不能转换

byte short char 如果和整型常量做算术运算会自动提升为int

float和浮点型自动会提升为double 浮点常量若未用f标识默认是double型

 

int 和long可以自动转为float, 虽然可能会丢失精度, 但是编译机器不报警

float自动转为double不丢失精度

 

long number = 2147483647777777L;

float  number1 = number;

System.out.println(number+"转换为了"+number1);//输出为2147483647777777转换为了2.14748365E15 可以看到数据精度丢失了

实际工作中对精度有要求的一般是使用Java.math.BigDicimal类