前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >原码 反码 补码

原码 反码 补码

原创
作者头像
Ritchie
修改2023-02-27 10:08:05
1.1K0
修改2023-02-27 10:08:05
举报
文章被收录于专栏:Ritchie的专栏Ritchie的专栏

众所周知,计算机中是以二进制来存储数据的,计算机顾名思义设计之初也是为了计算,那么计算机是如何进行运算的呢?

原码:二进制形式

反码:最高位符号保持不变,其余取反

补码:正数和0的补码等于原码,负数的补码将其对应正数按位取反再加1

1.计算机中的计算也都以补码来进行运算的, 那么为什么要引入这三种码呢?

以Java中的byte类型为例,byte类型占1B(8bit),取值范围为[-128, 127],这个取值范围是怎么来的呢?

0000 0000 ~ 1111 1111 这个区间是8b的取值范围共256,我们把最高位作为符号位,将其分为正数和负数来看:

如下是按照原码来区分:

0000 0000 ~ 0111 1111 => [0, 127]

1000 0000 ~ 1111 1111 => [-127, -0]

假如我们在原码上计算1 + (-1)

代码语言:javascript
复制
0000 0001
1000 0001
---------
1000 0010

出现的问题:

  • 0 有两种表示方式,有悖计算机中唯一性的原则
  • 使用原码正数和负数相加的结果错误, 1 + (-1) == -2 ??

如下是按照补码来区分:

0000 0000 ~ 0111 1111 => [0, 127]

1000 0000 ~ 1111 1111 => [-128,-1]

注意:补码1000 0001并不是-1,1000 0001 - 1 = 正数按位取反 => 0111 1111 = 正数,所以1000 0001对应的是-127

同理:补码1000 0000 - 1 = 正数按位取反 => 1000 0000 = 正数,所以1000 0000 对应的是-128, 同理1111 1111对应的-1

因此byte的取值范围为[-128, 127]

此时我们再执行1 + (-1), 可以看到结果为0, 使用补码之后上面的两个问题都得以解决

代码语言:javascript
复制
0000 0001
1111 1111
---------
0000 0000

总之:引入反码和补码之后,就可以解决负数运算的问题了

我们再以java中int的取值范围为例[-2147483648, 2147483647],首先通过如下的代码,打印出具体的补码

代码语言:javascript
复制
	// 这里打印的是补码的逆序
	public static void printTwosComplement(int target) {
		int value = target;
		for (int j = 0; j < 32; j ++ ) {
			System.out.print(value & 1);
			value >>= 1;
		}
		System.out.println();
		
	}
	public static void main(String[] args) {
		// 10000000000000000000000000000000
		printTwosComplement(-2147483648);
		// 10000000000000000000000000000001
		printTwosComplement(-2147483647);
		// 11111111111111111111111111111111
		printTwosComplement(-1);
		// 00000000000000000000000000000000
		printTwosComplement(0);
		// 01111111111111111111111111111110
		printTwosComplement(2147483646);
		// 01111111111111111111111111111111
		printTwosComplement(2147483647);
	}

按照上述所说:根据补码推断正数,因此对于-2147483648:

代码语言:javascript
复制
补码:    100000000 00000000 00000000 00000000
补码-1:  011111111 11111111 11111111 11111111
取反:    100000000 00000000 00000000 00000000

取反后得到的正数是2^31,也即2147483648

2. Java中 为什么两数相乘结果不对呢?两数相加结果是对的呢?

代码语言:javascript
复制
// e = 0.010000001
float e = 0.1f * 0.1f;
System.out.println(e);

// f = 0.2
float f = 0.1f + 0.1f;
System.out.println(f);

0.1的补码是多少呢?

代码语言:javascript
复制
0.1 * 2 = 0.2  ------ 0
0.2 * 2 = 0.4  ------ 0
0.4 * 2 = 0.8  ------ 0
0.8 * 2 = 1.6  ------ 1
0.6 * 2 = 1.2  ------ 1
0.2 * 2 = 0.4  ------ 0
0.4 * 2 = 0.8  ------ 0
......

以此类推,0.1的补码为 0.00011001100110011...... 计算机是无法准确表达0.1的,因此计算结果出现误差也可以理解, 但是为什么0.1 + 0.1的结果确是对的呢?

这个原因是Java编译器做了相关的优化,从而使结果为正确的

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档