Made by Mike_Zhang
博客名称及其域名变更提醒:
我的个人博客名称已由 UltraFisher 改为 UltraFish.
相应的域名已由ultrafisher.github.io改为ultrafish.cn.
后续所有的更新将会在ultrafish.cn进行, 本网站将停止更新.
十分感谢您的浏览以及支持, 让我们在UltraFish再次相遇!Notification
The name of my personal blog has been changed from UltraFisher to UltraFish.
Its domain name has been changed from ultrafisher.github.io to ultrafish.cn.
ONLY ultrafish.cn will be updated, this website will NOT be updated.
Thank you very much for your browsing and support, see you on the UltraFish!
Mike_Zhang
2020/10/15
(2020-09-24更新: 新增Excess)
(2020-09-24更新: 新增转换代码)
我在之前的文章 byte数据类型在显式类型转换时超出其取值范围的转换过程中提到里了计算机中原码,反码,补码等概念, 但是并没有仔细展开. 最近在学校的课程内容中又碰到了, 所以接下来具体来说一下.
数据在计算机中都是以二进制(binary)0和1的形式储存的, 但是在表示负数的时候, 并不能直接把“-”加在数字前面, 必须要用一些特定的方法来表示.
一般来说,有以下4种常用的表示负数的方法:
Signed Magnitude (原码);
One’s Complement (1的补码)(中文又称反码);
Two’s Complement (2的补码)(中文又称补码);
Excess (Biased)
(以下均以 8-bit 为例 )
Signed Magnitude
Signed Magnitude又称原码, 是用二进制数最高位(MSB)来表示符号, 0表示正号“+”, 1表示负号“-”, 剩下的位表示数值的绝对值
例如: (等号前为十进制数, 后为二进制数)
+20 = 00010100
-20 = 10010100
有意思的一点是, +0 = 00000000, -0 = 10000000.
对于8-bit来说, 用Signed Magnitude来表示, 其取值范围是-127 ~ +127.
如下表:
binary of: | Signed Magnitude |
---|---|
00000000 | 0 |
00000001 | 1 |
00000010 | 2 |
… | … |
01111111 | 127 |
10000000 | -0 |
10000001 | -1 |
… | … |
11111111 | -127 |
以下是Python代码实现:
1 | def signedMagnitude(numd): |
上面提到, 对于8-bit来说, 用Signed Magnitude来表示, 其取值范围是-127 ~ +127, 说明只能表示255个数, 但是8-bit应该是可以总共表示256个数的, 说明有一个数被浪费了.(0被表示了两次,+0和-0)
One’s Complement (反码)
One’s Complement (1的补码), 中文又称反码. 对于一个 n-bit 的数, 数x的one’s complement(反码)即为:2^n - 1 - x.
举例来说:
for x= 00111100
one’s complement of x = 2^8 - 1 - x = 100000000 - 1 - 00111100 = 11000011
可以发现, 00111100的one’s complement(反码)即为11000011, 简单来说就是把原来的1变成0, 0变成1, 这也是为说明把one’s complement叫做反码.
再举一个例子:
+20 = 00010100
-20 = 11101011
有意思的一点是, +0 = 00000000, -0 = 11111111
对于8-bit来说, 用one’s complement(反码)来表示, 其取值范围是-127 ~ +127.
如下表:
binary of: | Signed Magnitude | One’s Complement (反码 ) |
---|---|---|
00000000 | 0 | 0 |
00000001 | 1 | 1 |
00000010 | 2 | 2 |
… | … | … |
01111111 | 127 | 127 |
10000000 | -0 | -127 |
10000001 | -1 | -126 |
… | … | … |
11111111 | -127 | -0 |
以下是Python实现:
1 | def onesComplement(numd): |
和Signed Magnitude一样, 用one’s complement(反码)表示也会浪费一个数.(0被表示了两次,+0和-0)
Two’s Complement (2的补码)(中文又称补码)
Two’s Complement (2的补码), 中文又称补码. 对于一个 n-bit 的数, 数x的two’s complement(补码)即为:2^n - x. , 其实就是在反码上+1, 即, two’s complement(补码) = one’s complement(反码) + 1. 这样就可以解决one’s complement(反码)出现-0的情况, 使-128 ~ -1都能被表示, 不产生浪费.
举一个例子:
+20 = 00010100
one’s complement(反码) = 11101011
two’s complement(补码) = 11101011 + 1 = 11101100
-20 = 11101100
对于8-bit来说, 用two’s complement(补码)来表示, 其取值范围是-128 ~ +127.
binary of: | Signed Magnitude | One’s Complement (反码) | Two’s Complement(补码 ) |
---|---|---|---|
00000000 | 0 | 0 | 0 |
00000001 | 1 | 1 | 1 |
00000010 | 2 | 2 | 2 |
… | … | … | … |
01111111 | 127 | 127 | 127 |
10000000 | -0 | -127 | -128 |
10000001 | -1 | -126 | -127 |
… | … | … | … |
11111111 | -127 | -0 | -1 |
以下是Python实现:
1 | def twosComplement(numd): |
如何快速获得补码:
以-12为例
- 写出其绝对值的二进制数: 00001100
- 从右向左开始, 找到第一个1(00001 1 00), 反转其左边所有位(11110 1 00)并保持其右边位不变
因此:
+12 = 00001100
-12 = 11110100
注意
原码、反码、补码是用作负数的二进制表示, 对于正数的表示没有影响, 从下面的表格就可以看出这一点:
binary of: | Signed Magnitude | One’s Complement (反码) | Two’s Complement(补码) |
---|---|---|---|
00000000 | 0 | 0 | 0 |
00000001 | 1 | 1 | 1 |
00000010 | 2 | 2 | 2 |
… | … | … | … |
01111111 | 127 | 127 | 127 |
10000000 | -0 | -127 | -128 |
10000001 | -1 | -126 | -127 |
… | … | … | … |
11111111 | -127 | -0 | -1 |
但是接下来的Excess方法对被转换的所有整数都有影响.
Excess (Biased)
excess与上面所说的三种方法有很大的不同, 最明显的不同就是, 在excess方法中, 最高位的1代表正数, 0代表负数, 以下是excess的转换方法:
- 在原来的数字上加上一个常量(bias);
- 再把得出来的数转成二进制.
举例:
Excess 128 表示加的数是128;
+12 -> +12 + 128 = 140 = 10001100
-12 -> -12 + 128 = 116 = 01110100
0 -> 0 + 128 = 128 = 10000000
…
对于8-bit来说, 用Excess128来表示, 其取值范围是-128 ~ +127.
binary of: | Signed Magnitude | One’s Complement (反码) | Two’s Complement(补码) | Excess 128 |
---|---|---|---|---|
00000000 | 0 | 0 | 0 | -128 |
00000001 | 1 | 1 | 1 | -127 |
00000010 | 2 | 2 | 2 | -126 |
… | … | … | … | |
01111111 | 127 | 127 | 127 | -1 |
10000000 | -0 | -127 | -128 | 0 |
10000001 | -1 | -126 | -127 | 1 |
… | … | … | … | |
11111111 | -127 | -0 | -1 | 127 |
以下是Python实现:
1 | def excessEightbits(numd): |
从上面这个表可以看出, 用excess的方法能够让二进制数和十进制数保持一样的大小顺序, 这样能够在之后计算时保持和值和差的的统一,方便计算.
总的来说, excess方法是让负号“消失”, 通过给每个数加上一个数, 让某一范围内的数整体向正数方向移动, 直到没有负数. 而移动的长度就是加的数的大小.
对于一个N-bit的数, 一共可以表示2^N个数, 其中有 2^(N-1) 个负数, 2^(N-1) 个正数(包括0), 能够表示的范围就是 -2^(N-1) ~ 2^(N-1)-1.
代码分享
以下Python代码实现用四种方法使整数在二进制数和十进制数之间转换
1 | #Made by Mike_Zhang |
写在最后
原码、反码、补码中可以深挖的东西还有好多,都十分有趣,之后会继续记录。
最后,希望大家一起交流,分享,指出问题,谢谢!
原创文章,转载请标明出处
Made by Mike_Zhang