从“追求尽量不出错”,到正视“出错是必|

如此而已~~~

园龄:3年3个月粉丝:0关注:12

32_Java中的进制

Java的进制

一、进制基础知识

进制概述:

​ 指进位制,是一种计数方法,也称为进位计数法或位值计数法

十进制:0, 1, 2, 3, 4, 5, 6, 7, 8, 9

R进制:由(0~(R-1))组成,并且每一位上的数据逢R进1

我们的计算机是由逻辑电路组成,逻辑电路通常只有两个状态:开关的接通与断开

这两种状态正好就可以使用”1“ 和 ”0“表示,也就是我们要讲解的组成二进制的数字符号

二进制:0, 1

虽然二进制更适用于计算机,但是二进制表示数据太长了

为了解决这个问题,八进制和十六进制就来了

八进制:0, 1, 2, 3, 4, 5, 6, 7

十六进制:0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F(为了节省占位:1015改为了AF(大小写均可)使其只占用一个字节)

举例:

二进制:10000000000

八进制:2000

十六进制:400

进制越大,表现形式越短

问题:

10到底是几?

在Java中:

二进制:以0b开头

八进制:以0开头

十六进制:以0x开头

十进制:默认使用

练习:在控制台输出下面的数据,看结果是多少

0b10000000000

02000

0x400

参考代码:

package com.itheima;
/*
练习:在控制台输出下面的数据,看结果是多少
0b10000000000
02000
0x400
*/
public class Demo01 {
public static void main(String[] args){
//从输出结果来看都是1024,在控制台输出的数字默认都是十进制数字
System.out.println(0b10000000000);
System.out.println(02000);
System.out.println(0x400);
}
}

二、进制转换

1、常见的进制转换:

R进制到十进制的转换

规则:按权展开法(系数*基数的权次幂相加)

​ 系数:每一位上的数

​ 基数:R进制,基数就是R

​ 权:从数值的右侧,以0开始编号,对应位上的编号就是该位上的权

举例:0b101010

0b101010 = 1*2^5 + 0*2^4 + 1*2^3 + 0*2^2 + 1*2^1 + 0*2^0 = 32 + 0 + 8 + 0 + 2 + 0 = 42
系数:对应的每一位数:1、0、1、0、1、0
基数:根据0b判断是二进制,R = 2
权:从右往左依次从0开始步长为1累加

十进制到R进制的转换

规则:重复相除法(除基取余,直到商为0,余数反着写到位数)

把42转换为二进制,每行为每一次做除法,上一步的商做下一步的被除数

十进制数据 基数 余数
42 2 21 0
21 2 10 1
10 2 5 0
5 2 2 1
2 2 1 0
1 2 0 1

得到:

42 = 0b101010

2、进制的快速转换:

如何实现二进制和十进制之间的快速转换呢?

采用8421码就可以实现

8421码:

是BCD代码中最常用的一种

BCD:(Binary-Coded Decimal)二进制码十进制数,在这种方法中,每一位二进制值代码的1都是代表一个固定数值,把每一位的1代表的十进制数加起来得到的结果就是它所代表的十进制数码。

二进制: 1 1 1 1 1 1 1 1
对应的十进制数值: 128 64 32 16 8 4 2 1

举例:

0b101010 = 42

0b1010101 = 85

十进制到二进制? 通过使用88减去表中能够包括的最大值,如 -64后剩余24,此位数为1;而后32不足补0;继续进行。

88 = 0b1011000

二进制到八进制如何转换?

思路1:将二进制转换为十进制,然后再把该十进制转换为八进制

思路2:三位组合法

把二进制的数据,从右开始,每三位一组和(即三位并一位),最左边不够的时候,补0

然后,分别计算对应的十进制数值

最后,从左往右,把每个十进制的数据组合起来,就是一个八进制数据

二进制:0b101010

拆分: 101 和 010

二进制 000 001 010 011 100 101 110 111
十进制 0 1 2 3 4 5 6 7

计算得出:5 和 2

组合: 52

最后得出八进制: 052

反之将八进制转换为二进制是一位拆三位

如:052 -> 5 和 2

得到: 101 和 010

最后得出: 0b101010

二进制到十六进制如何转换?

四位组合法:

​ 把二进制的数据,从右开始,每四位一组合,最左边不够的时候,补0

​ 然后,分别计算对应的十进制数值

​ 最后,从左往右,把每个十进制的数据组合起来,就是一个十六进制数据

二进制:0b101010

拆分: 0010 和 1010

二进制 0000 0001 0010 0011 0100 0101 0110 0111
十进制 0 1 2 3 4 5 6 7
二进制 1000 1001 1010 1011 1100 1101 1110 1111
十进制 8 9 10 11 12 13 14 15

得到: 2 和 10(A)

组合: 2A

十六进制: 0x2A

反之从十六进制到二进制一位拆四位

如:0x2A

拆分: 2 和 A

得到: 0010 和 1010

组合: 00101010

得到二进制:0b101010

3、Java内置的进制转换:

java.lang.Integer类中的静态方法:

public static String toBinaryString(int i):在基数2中返回整数参数的字符串表示形式为无符号整数

public static String toOctalString(int i):在基数8中返回整数参数的字符串表示形式为无符号整数

public static String toHexString(int i):返回整数参数的字符串表示形式,作为16位中的无符号整数

public static String toString(int i, int radix):返回由第二个参数指定的基数中的第一个参数的字符串表示形式

参考代码:

package com.itheima;
/*
java.lang.Integer类中的静态方法:
public static String toBinaryString(int i):在基数2中返回整数参数的字符串表示形式为无符号整数
public static String toOctalString(int i):在基数8中返回整数参数的字符串表示形式为无符号整数
public static String toHexString(int i):返回整数参数的字符串表示形式,作为16位中的无符号整数
*/
public class Demo02 {
public static void main(String[] args){
//一、、十进制转换为:二、八、十六进制
//public static String toBinaryString(int i):在基数2中返回整数参数的字符串表示形式为无符号整数
System.out.println(Integer.toBinaryString(42));
//public static String toOctalString(int i):在基数8中返回整数参数的字符串表示形式为无符号整数
System.out.println(Integer.toOctalString(42));
//public static String toHexString(int i):返回整数参数的字符串表示形式,作为16位中的无符号整数
System.out.println(Integer.toHexString(42));
System.out.println("----------");
//二、、进制转换为以后一个参数为基数的进制
//public static String toString(int i, int radix):返回由第二个参数指定的基数中的第一个参数的字符串表示形式
System.out.println(Integer.toString(0b101010, 10));
System.out.println(Integer.toString(052, 10));
System.out.println(Integer.toString(0x2A, 10));
System.out.println(Integer.toString(0x2a, 10));
System.out.println("----------");
//使用第二个中使用的方法实现任意进制之间的转换
System.out.println(Integer.toString(0b101010, 8));
System.out.println(Integer.toString(0b101010, 16));
}
}

三、有符号数据表示法

十进制的数据:

​ +表示正数

​ -表示负数

计算机中的数据:

​ 0表示正数

​ 1表示负数

而对于计算机识别的数据来说,0和1本身也表示数据值,那么我们怎么判断它是数值位还是符号位呢?

规定:符号位位于数值第一位

比如说:二进制数0b101010。用一个字节来存储(注意:1字节 = 8位)

0010 1010 (缺位补0,首位0表示正数)

那么我们如何表示:-0b101010,用一个字节来存储数据呢?

1010 1010 (缺位补0,首位1表示负数)

原码表示法:是最简单的及其数表示法。用最高位表示符号位,1表示负号,0表示正号。其余位标识数值大小。

把数据1到7和数据-1到-7用原码表示(用一个字节存储)

数据 1 2 3 4 5 6 7
正数 0 0000001 0 0000010 0 0000011 0 0000100 0 0000101 0 0000110 0 0000111
负数 1 0000001 1 0000010 1 0000011 1 0000100 1 0000101 1 0000110 1 0000111

计算:1 + 2, 1 - 2

1 + 2 = 00000001 + 00000010 = 00000011 = 3

1 - 2 = 1 + (-2) = 00000001 + 10000010 = 10000011 = -3 显然这个结果是错误的!!!

由此出现了反码表示法:

​ 正数的反码和原码相同

​ 负数的反码就是它的原码除符号位外,按位取反(即1变0,0变1)

把数据1到7和数据-1到-7用反码表示(用一个字节存储)

数据 1 2 3 4 5 6 7
正数 0 0000001 0 0000010 0 0000011 0 0000100 0 0000101 0 0000110 0 0000111
负数原码 1 0000001 1 0000010 1 0000011 1 0000100 1 0000101 1 0000110 1 0000111
负数反码 1 1111110 1 1111101 1 1111100 1 1111011 1 1111010 1 1111001 1 1111000

计算:1 - 2, -1 - 2

1 - 2 = 1 + (-2) = 00000001 + 11111110 = 11111110 = -1

-1 - 2 = (-1) + (-2) = 11111110 + 11111101 = 11111011(符号位进位舍弃)= -4 显然还是有问题

最后采用补码表示法:

​ 正数的补码和原码相同

​ 负数的补码等于反码+1

把数据1到7和数据-1到-7用补码表示(用一个字节存储)

数据 1 2 3 4 5 6 7
正数 0 0000001 0 0000010 0 0000011 0 0000100 0 0000101 0 0000110 0 0000111
负数原码 1 0000001 1 0000010 1 0000011 1 0000100 1 0000101 1 0000110 1 0000111
负数反码 1 1111110 1 1111101 1 1111100 1 1111011 1 1111010 1 1111001 1 1111000
负数补码 1 1111111 1 1111110 1 1111101 1 1111100 1 1111011 1 1111010 1 1111001

计算: 1 - 2,-1-2

1 - 2 = 1 + (-2) = 00000001 + 11111110 = 11111111 = -1 (结果正确)

-1 - 2 = (-1) + (-2) = 11111111 + 11111110 = 11111101(符号位进位舍弃) = -3 (结果正确)

四、整数强制转换之数据溢出

请问下列代码是否有问题?如果有问题,如何解决呢?解决完毕之后,结果是多少呢?

​ byte b = 130; (130是默认为整型数据)

​ byte b = (byte)130; (使用强制转换)

二进制: 1 1 1 1 1 1 1 1
对应的十进制数值: 128 64 32 16 8 4 2 1

130 = 0b10000010 先转换位二进制数据,一字节八位

但是默认为整型int(4字节:最高位符号位,缺位补0)

130原码:0 0000000000000000000000010000010

130补码:0 0000000000000000000000010000010 先通过数据默认表示形式获取默认补码(计算机底层使用补码进行计算,但是以原码呈现)

进行强制转换(即由四字节变为,一字节剩余八位,且以补码形式)

补码:1000 0010 最高位为1,即负数

通过补码获得反码:1000 0001 即:在补码基础上减一

通过反码获得原码:1 111 1110 即:符号位不变,其余位按位求反

得到对应的十进制结果为:-126

显然是有问题的,尽管通过强制转换使得编译通过,但是结果与实际不符合。

练习:将130改为300

整型原码:0000 0000 0000 0000 0000 0001 0010 1100

整型补码:0000 0000 0000 0000 0000 0001 0010 1100

强制转换为字节:(补码)0 010 1100 此处最高位为0,是正数原、反、补相同

反码:00101100

原码:00101100

结果为:44

五、浮点数存储

前面所讲的进制转换都是整数部分,现在了解一下小数部分

十进制浮点数和二进制浮点数的转换:

​ 二进制浮点数转换为十进制浮点数

​ 十进制浮点数转换为二进制浮点数

二进制浮点数转换为十进制浮点数

规则:按权展开法(系数 * 基数的权次幂相加)

​ 系数:每一位上的数

​ 基数:对应的进制数

​ 权:

​ 整数部分:从小数点左侧,以0开始编号,对应位上的编号就是该位上的权

​ 小数部分:从小数点右侧,以-1开始编号,对应位上的编号就是该位上的权

举例:把二进制浮点数110.101转换为十进制浮点数

可以先将其分为整数部分和小数部分

0b110 ---> 6

0b0.101 ---> 0.625

110.101 = 1 * 2^2 + 1 * 2^1 + 0 * 2^0 + 1 * 2^(-1) + 0 * 2^(-2) + 1 * 2^(-3)

​ = 4 + 2 + 0 + 0.5 + 0 + 0.125

​ = 6.625

即:6.625

十进制浮点数转换为二进制浮点数转换

规则:整数部分重复相除法,小数部分重复相乘法

​ 整数部分重复相除法:除基取余,直到商为0,余数反转

​ 小数部分重复相除法:乘基取整,直到小数为0或者达到指定精确度位,整数顺序排列

举例:把十进制浮点数6.625转换为二进制浮点数

整数部分:

十进制数 基数 余数
6 2 3 0
3 2 1 1
1 2 0 1

小数部分:

十进制小数 基数 取整
0.625 2 1.25 1
0.25 2 0.5 0
0.5 2 1.0 1

整数部分反转,小数部分顺序排列:

110.101

浮点数存储:

根据国际标准IEEE 754,任意一个二进制浮点数都可以表示为:V = (-1)^s * M * 2 ^ E

​ s表示符号位,当s = 0,V为正数;当s = 1,V为负数

​ M表示有效数字

​ E表示指数位

​ 2表示此处使用的是二进制

举例:十进制的浮点数6.625,写成二进制是110.101,相当于1.10101 * 2 ^ 2(类似于十进制的科学计数法)

​ 按照上面V的格式,可以得出 s = 0, M = 1.10101, E = 2

IEEE 754 对浮点数存储的规定:

​ 对于32位的浮点数,也就是float类型的,最高的1位是符号位s,接着的8位是指数E,剩下的23位为有效数字M

​ 对于64为的浮点数,也就是double类型的,最高位的1位符号位s,接着的11位是指数E,剩下的52位为有效数字M

IEEE 754对有效数字M和指数E,还有一些特别的规定:

​ 规范化表示的时候,M要写成1.xxxxxx的形式,其中xxxxxx表示小数部分

​ IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分

​ 比如保存1.01的时候,只保存01,等到读取的时候,再把第一位的1加上去

​ 指数E,为一个无符号整数(unsigned int)。这意味着,如果E为8位,它的取值范围为0~255;如果E为11位,它的取值范围为

​ 0~2047但是,我们直到,科学计数法中的E是可以出现负数的,所以IEEE 754规定,E的真实值必须再减去一个中间数,对于8位的

​ E,这个中间数是127;对于11位的E,这个中间数是1023

​ 比如,2^10的E是10,所以保存成32位浮点数是,必须保存成10 + 127 = 137,即10001001

举例:浮点数6.625F的存储,F表示此为:float

二进制:110.101

规范化形式表示:1.10101000000000000000000 * 2 ^ 2 23位表示E缺位补0

​ 符号:s 为 0

​ 有效数字:M:10101000000000000000000 23位

​ 指数E:2 + 127 = 129 = 10000001 8位

最终存储形式

​ 0 10000001 1010100000000000000000 ---> 对应 s E M

六、浮点数运算之结果有误

存储时采用:s E M 的形式。

以下是计算过程,在此过程中都只是在规范化的基础上进行计算,计算机最终会将计算结果的规范化表示形式以IEEE形式存储

请问下面程序的运行结果是多少?

System.out.println(2.0F - 1.5F);

System.out.println(2.0F - 1.3F);

System.out.println(2.0F - 1.4F);

结果是:

0.5

0.7

0.6

实际运行结果:

0.5
0.70000005
0.6

逐个分析:

System.out.println(2.0F - 1.5F);

十进制:2.0F

二进制:10.0

规范化表示:1.00000000000000000000000 * 2 ^ 1

十进制:1.5F

二进制:1.1

规范化表示:1.10000000000000000000000 * 2 ^ 0

计算:

1.00000000000000000000000 * 2 ^ 1

1.10000000000000000000000 * 2 ^ 0 ---> 0.1100000000000000000000 * 2 ^ 1

结果是:0.0100000000000000000000000 * 2 ^ 1

结果的规范化表示:1.00000000000000000000000 * 2 ^ -1

0.5F的规范化表示?

二进制:0.1

规范化表示:1.00000000000000000000000 * 2 ^ -1 (与计算结果相同)


System.out.println(2.0F - 1.3F);

十进制:2.0F

二进制:10.0

规范化表示:1.00000000000000000000000 * 2 ^ 1

十进制: 1.3F

二进制:1.01001 1001 1001 1001 1001 1001……

规范化表示:1.01001100110011001100110 * 2 ^ 0 (截取有效数字为23位)

计算:

1.00000000000000000000000 * 2 ^ 1

1.01001100110011001100110 * 2 ^ 0 ---> 0.101001100110011001100110 * 2 ^ 1

0.010110011001100110011010 * 2 ^ 1 (结果)

结果的规范化表示:1.01100110011001100110100 * 2 ^ -1

0.7F的规范化表示?

二进制:0.10110 0110 0110 0110 0110 0110......

规范化表示:1.01100110011001100110011 * 2 ^ -1 (与计算结果不符合,末尾三位不匹配)


System.out.println(2.0F - 1.4F);

十进制:2.0F

二进制:10.0

规范化表示:1.00000000000000000000000 * 2 ^ 1

十进制:1.4F

二进制:1.0110 0110 0110 0110 0110 0110 0110......

规范化表示:1.01100110011001100110011 * 2 ^ 0 有效数字截取23位

计算:

1.00000000000000000000000 * 2 ^ 1

1.01100110011001100110011 * 2 ^ 0 ---> 0.101100110011001100110011 * 2 ^ 1

0.010011001100110011001101* 2 ^ 1 (结果)

结果的规范化表示:1.00110011001100110011010 * 2 ^ -1

0.6F的规范化表示?

二进制:0.1001 1001 1001 1001 1001 1001 1001......

规范化表示:1.00110011001100110011010 * 2 ^ -1 (与计算结果一致)

注意(0舍1入):即当截取23位数时,第24位为0直接舍去,但是如果为1需要向前进一位

浮点数运算,在存储的运算过程中都可能会有精度的丢失,故出现的结果和我们以为的结果就会有出入

在实际开发中不太建议直接使用float 或者double做运算

那么,我们到底如何避免这种精度丢失问题呢?

使用java.math.BigDecimal中的方法即可

构造方法:BigDecimal(String val)

成员方法:public BigDecimal subract(BigDecimal subtrahend):减法操作

其他方法,参考文档

参考代码:

package com.itheima;
import java.math.BigDecimal;
/*
浮点数运算
*/
public class Demo04 {
public static void main(String[] args){
System.out.println(2.0F - 1.5F);
System.out.println(2.0F - 1.3F);
System.out.println(2.0F - 1.4F);
BigDecimal bd1 = new BigDecimal("2.0");
BigDecimal bd2 = new BigDecimal("1.3");
System.out.println(bd1.subtract(bd2));
}
}

本文作者:如此而已~~~

本文链接:https://www.cnblogs.com/fragmentary/p/17019865.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   如此而已~~~  阅读(378)  评论(0编辑  收藏  举报
//雪花飘落效果
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起