《Java 编程的逻辑》笔记——第2章 理解数据背后的二进制

声明:

本博客是本人在学习《Java 编程的逻辑》后整理的笔记,旨在方便复习和回顾,并非用作商业用途。

本博客已标明出处,如有侵权请告知,马上删除。

开头语

在第 1 章,我们遗留了几个问题。

  • 正整数相乘的结果过居然出现了负数。
  • 非常基本的小数运算结果居然不精确。
  • 字符类型也可以进行算术运算和比较。

要理解这些行为,我们需要理解数值和文本字符在计算机内部的二进制表示,本章就来介绍各种数据背后的二进制。

2.1 整数的二进制表示与位运算

2.1.1 十进制

要理解整数的二进制,我们先来看下熟悉的十进制。十进制是如此的熟悉,我们可能已忽略了它的含义。比如 123,我们不假思索就知道它的值是多少。

但其实 123 表示的 1 * (10 ^ 2) + 2 * (10 ^ 1) + 3 * (10 ^ 0),它表示的是各个位置数字含义之和,每个位置的数字含义与位置有关,从右向左,第一位乘以 10 的 0 次方, 即 1,第二位乘以 10 的 1 次方,即 10,第三位乘以 10 的 2 次方,即 100,依次类推。

换句话说,每个位置都有一个位权,从右到左,第一位为 1,然后依次乘以 10,即第二位为 10,第三位为 100,依次类推。

2.1.2 正整数的二进制表示

正整数的二进制表示与此类似, 只是在十进制中,每个位置可以有 10 个数字,从 0 到 9,但在二进制中,每个位置只能是 0 或 1。位权的概念是类似的,从右到左,第一位为 1,然后依次乘以 2,即第二位为 2,第三位为 4,依次类推。

表 2-1 列出了一些数字的二进制与对应的十进制。

在这里插入图片描述

2.1.3 负整数的二进制表示

十进制的负数表示就是在前面加一个负数符号 -,例如 -123。但二进制如何表示负数呢?

其实概念是类似的,二进制使用最高位表示符号位,用 1 表示负数,用 0 表示正数

但哪个是最高位呢?整数有四种类型,byte/short/int/long,分别占 1/2/4/8 个字节,即分别占 8/16/32/64 位,每种类型的符号位都是其最左边的一位。

为方便举例,下面假定类型是 byte,即从右到左的第 8 位表示符号位。

但负数表示不是简单的将最高位变为 1,比如说:

  • byte a = -1,如果只是将最高位变为 1,二进制应该是 10000001,但实际上,它应该是 11111111。
  • byte a= -127,如果只是将最高位变为 1,二进制应该是 11111111,但实际上,它却应该是 10000001。

和我们的直觉正好相反,这是什么表示法?这种表示法称为补码表示法,而符合我们直觉的表示称为原码表示法补码表示就是在原码表示的基础上取反然后加 1。取反就是将 0 变为 1,1 变为 0。

负数的二进制表示就是对应的正数的补码表示,比如说:

  • -1:1 的原码表示是 00000001,取反是 11111110,然后再加 1,就是 11111111。
  • -2:2 的原码表示是 00000010,取反是 11111101,然后再加 1,就是 11111110。
  • -127:127 的原码表示是 01111111,取反是 10000000,然后再加 1,就是 10000001。

给定一个负数二进制表示,要想知道它的十进制值,可以采用相同的补码运算。比如:10010010,首先取反,变为 01101101,然后加 1,结果为 01101110,它的十进制值为 110,所以原值就是 -110。直觉上,应该是先减 1,然后再取反,但计算机只能做加法,而补码的一个良好特性就是,对负数的补码表示做补码运算就可以得到其对应整数的原码,正如十进制运算中负负得正一样。

byte 类型,正数最大表示是 01111111,即 127,负数最小表示(绝对值最大)是 10000000,即 -128,表示范围就是 -128 到 127。其他类型的整数也类似,负数能多表示一个数。

负整数为什么采用补码呢?

原因是:只有这种形式,计算机才能实现正确的加减法

计算机其实只能做加法,1-1 其实是 1+(-1)。如果用原码表示,计算结果是不对的。比如说:

1  -> 00000001
-1 -> 10000001
+ ------------------
-2 -> 10000010

用符合直觉的原码表示,1-1 的结果是 -2。如果是补码表示:

1  -> 00000001
-1 -> 11111111
+ ------------------
0  ->  00000000 

结果是正确的。再比如,5-3:

5  -> 00000101
-3 -> 11111101
+ ------------------
2  ->  00000010 

结果也是正确的。就是这样的,看上去可能比较奇怪和难以理解,但这种表示其实是非常严谨和正确的,是不是很奇妙?

理解了二进制加减法,我们就能理解为什么正数的运算结果可能出现负数了。当计算结果超出表示范围的时候,最高位往往是 1,然后就会被看做负数。比如说,127+1:

127   -> 01111111
1     -> 00000001
+ ------------------
-128  -> 10000000 

计算结果超出了 byte 的表示范围,会被看做 -128。

2.1.4 十六进制

二进制写起来太长,为了简化写法,可以将四个二进制位简化为一个 0 到 15 的数,10 到 15 用字符 A 到 F 表示,这种表示方法称为十六进制,如表 2-2 所示。

在这里插入图片描述

可以用十六进制直接写常量数字,在数字前面加 0x 即可。比如 10 进制的 123,用十六进制表示是 0x7B,即 123 = 7 * 16+11。给整数赋值或者进行运算的时候,都可以直接使用十六进制,比如:

int a = 0x7B;

Java7 之前不支持直接写二进制常量。比如,想写二进制形式的 11001,Java7 之前不能直接写,可以在前面补 0,补足 8 位,为 00011001,然后用十六进制表示,即 0x19。Java7 开始支持二进制常量,在前面加 0b 或 0B 即可,比如:

int a = 0b11001;

在 Java 中,可以方便的使用 Integer 和 Long 的方法查看整数的二进制和十六进制表示,例如:

int a = 25;
System.out.println(Integer.toBinaryString(a)); //二进制
System.out.println(Integer.toHexString(a));  //十六进制
System.out.println(Long.toBinaryString(a)); //二进制
System.out.println(Long.toHexString(a));  //十六进制

2.1.5 位运算

位运算是将数据看做二进制,进行位级别的操作。Java7 之前不能单独表示一个位,但是可以用 byte 表示 8 位,可以用 16 进制写二进制常量。比如:0010 表示成 16 进制是 0x2, 110110 表示成 16 进制是 0x36。

位运算有移位运算逻辑运算

移位有:

  • 左移:操作符为 <<,向左移动,右边的低位补 0,高位的就舍弃掉了。将二进制看做整数,左移 1 位就相当于乘以 2。
  • 无符号右移:操作符为 >>>,向右移动,右边的舍弃掉,左边补 0。
  • 有符号右移:操作符为 >>,向右移动,右边的舍弃掉,左边正数高位补 0,负数高位补 1。将二进制看做整数,右移 1 位相当于除以 2。

例如:

int a = 4; // 100
a = a >> 2; // 001,等于1
a = a << 3 // 1000,变为8

逻辑运算有:

  • 按位与 &:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bm1998

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值