面试题在文章第4节。在看面试题之前,可以先看一下1-3节的知识点。
1. 补码
Two's Complement(二补数、补码)是对二进制数
的数学运算,运算过程为:对二进制序列每一位取反(0->1; 1->0),再加1。
bits | 取反 | 补码 |
---|---|---|
011 | 100 | 101 |
010 | 101 | 110 |
111 | 000 | 001 |
2. 计算机中有符号数的表示
计算机中的数值类型分为整数型和浮点数型,有符号数在最高位设置符号位,其余低位均为数值位。数值位一律采用补码形式存储,并参与计算。采用补码的形式表示有符号数至少有两大好处。
- 符号位和数值位统一参与运算,不用区分正、负,加法和减法实现简单;
- 数据的原码和补码之间的相互转换不需要依赖额外硬件电路。
下面分别介绍有符号数的表示方法
2.1 整数
正整数
正整数的补码是其二进制表示,与原码相同。
例如,在整数类型占用4字节(32位)的系统中,+5的补码是00000000 00000000 00000000 00000101
。最高位0
表示该数值为正数,其余31位表示数值大小。
负整数
负整数的补码需要对其绝对值的二进制表示进行补码运算。
例如,-5的补码是11111111 11111111 11111111 11111011
。最高位为1
,表示该数值为负数,其余31位表示数值大小。
在进行运算时,CPU并不会区分是正数还是负数,而是直接进行计算,这正是前面介绍的符号位和数值位的统一。
例如,a=10, b=-5
,则a+b
的运算过程如下:
00000000 00000000 00000000 00001010 (10)
+ 11111111 11111111 11111111 11111011 (-5)
===========================================
00000000 00000000 00000000 00000101 (5)
如果,a=1, b=5
,则a-b
首先转换成加法a+(-b)
,再进行计算,过程如下:
a-b ==> a+(-b)
00000000 00000000 00000000 00000001 (1)
+ 11111111 11111111 11111111 11110110 (-10)
===========================================
11111111 11111111 11111111 11110111 (-9)
对于正整数(最高位为1),将非符号位的二进制位直接转换成十进制,就表示该正数的实际大小。如果一个数是负整数,如何将其补码转换成十进制大小呢?补码运算即可。
例如上面的11111111 11111111 11111111 11110111
,最高位符号位是1
,所以该数为负数,补码运算之后为00000000 00000000 00000000 00001001
,大小为9,所以表示-9
。
2.2 浮点数
pass
3. 为什么是补码?
为什么两个数相减a-b
用补码形式a+(-b)
进行计算的结果是正确的?不妨看一下对b进行补码的过程绝对值的二进制序列取反,再加1
。取反在计算机的逻辑电路中就是开关的闭合状态取反即可,即1->0,0->1。如果用数学算式表达的话,对一个bit位b的取反运算可以写成
取反b = 1-b (*)
b=0时,取反b为1,1-b=1;
b=1时,取反b为0,1-b=0;
所以算是(*)可以表达取反运算
综上,a-b
的计算过程可表达为(8bit为例)
a-b == a+(-b) == a+(11111111-b+1) == a+(b的补码形式)
在8bit系统中,11111111 + 1 == 00000000,溢出。
所以,a+(11111111-b+1) = a+(0-b) = a - b
可以看出,补码运算的实现效果巧妙地利用了因计算机系统位数限制而产生的溢出现象。
4. 一个C++面试题
下面代码打印多少?
#include <iostream>
int main(int argc, char **argv)
{
std::cout << 25u - 50;
return 0;
}
答案是4294967271
。
25u
是unsigned int
类型,50为int
类型。在这两种操作数进行-
运算时,int
被提升为unsigned int
型,运算变为25u - 50u
,结果也应该是unsigned int
类型。经过对-50u
进行补码运算后带入加法运算,-25
的二进制表示形式被存入内存,即11111111 11111111 11111111 11100111
(int为32位),在打印时按无符号数处理,则直接转换成十进制正整数为4294967271
。
11111111 11111111 11111111 11100111 =
2^31 + 2^30 + ... + 2^5 + 2^2 + 2^1 + 2^0 =
2^5(1-2^27) / (1-2) + 7 =
4294967271
更多面试题和答案:24 Essential C++ Interview Questions
网友评论