编程基础(一)——计算机中的数

目录

一、综述

二、整数

2.1 整数的表示

2.1.1 ones' complement & two's complement

2.1.2 有符号数和无符号数的转化关系

2.1.3 数的扩展

2.2 整数的运算

2.2.1 无符号加法

2.2.1 补码加法

三、数的特性对C编程的影响

3.1 无符号数和有符号数

3.1.1  数

3.1.1 cast

3.1.2 extension

3.1.3 shift

3.1.4 overflow

3.1.5 C中的余数

四、参考


一、综述

计划对程序中一些细节背后的机制进行记录,本篇相当于CSAPP第二章笔记,没有追求面面俱到,只是重读CSAPP时将自己有些遗忘的或者不是很清楚的概念进行梳理,备忘。

二、整数

2.1 整数的表示

2.1.1 ones' complement & two's complement

先介绍百度百科(https://baike.baidu.com/item/%E8%A1%A5%E7%A0%81/6854613?fr=aladdin)模的概念:

“模”是指一个计量系统的计数范围。如时钟等。计算机也可以看成一个计量机器,它也有一个计量范围,即都存在一个“模”。例如:

时钟的计量范围是0~11,模=12。表示n位的计算机计量范围是0~2^(n)-1,模=2^(n)。

“模”实质上是计量器产生“溢出”的量,它的值在计量器上表示不出来,计量器上只能表示出模的余数。任何有模的计量器,均可化减法加法运算。

如对于一个8bit的数的计量器,其模为2^8,由于模在计量器上表示不出来,那么a + b = a + b + n * 2^8(n是整数)

再介绍补码的概念:补码来源于以下思想——如果一个正整数为A,那么其对应的负整数可以用-A来表示,可以用 -A = 0 - A来得到,由于0需要借位,在计算机的二进制表示中(假设数长度w bit) -A用 2^w - A表示,称为补码,上述表达式只有一个‘2’,因此称为two‘s complement区别于ones’ complement,反码来源于对原码取反,其结果相当于用[1111...11] - A,所以称为ones' complement(注意两种表示的 ‘s和s‘)。

所以,计算机中的整数采用补码表示,主要原因是

  1. 相比于ones‘ complement有+0和-0这种编码,补码中的0只有一个。
  2. 补码可以直接进行运算, 计算机中的数都是计数范围有限的计量系统(假设模2^w),因此X-Y 可以根据模的性质转换成X -Y+2^w = X + (2^w - Y),即整数的减法可以转换成加上Y的补码,而补码本身又可以通过取反加一实现,简化了电路设计。一个减法过程的示意:

补码的数学表达式也是理解补码的一种方式:最高位的权重是-2^(w - 1)

2.1.2 有符号数和无符号数的转化关系

记住下面两个公式就行了

2.1.3 数的扩展

在不同的字长的整数之间进行转换,分为以下两种:

  1. zero extension(零扩展),如将无符号数转换为更大的数据类型,补0
  2. sign extension(符号扩展),将一个补码数字转换为更大的数据类型,扩展符号

TIPS:一个负数前面的1都可以忽略,因此0xfffffffc,可以看做4-bit 1100(c),即 -8 + 4 = -4

2.2 整数的运算

2.2.1 无符号加法

2.2.1 补码加法

三、数的特性对C编程的影响

3.1 无符号数和有符号数

3.1.1  数

C中的数值默认是有符号的,如果指定是无符号的需要使用U后缀

3.1.1 cast

分为显示转换和隐式转换。

注意隐式转换带来的特殊之处:对同时包含有符号和无符号的表达式,会将有符号的数转换成无符号的数处理,尤其当涉及<和>这样的关系表达式,要十分小心。

-1 < 0U      //0xFFFFFFFF < 0U 

0U - 1        // 0xFFFFFFFF      

4U - 5U     //  0xFFFFFFFF      

TIPS:  在CSAPP中有如下问题  

#define INT_MAX   2147483647
#define INT_MIN   (-INT_MAX - 1)

这里INT_MIN不定义成-2147483648或者0x80000000的原因是-2147483648是一个表达式,其中2147483648超出的int上限,于是将该数解释成了long int类型(按照int/long int/long long int 匹配数字大小),再取-就达不到目的了。因此用(-INT_MAX - 1)实现。

3.1.2 extension

数进行扩展时,一定是先改变大小,再做有符号/无符号的变化,写代码时最好避免隐式转换,如short ->unsigned,实际的转化过程unsigned = (unsigned)(int)short。

3.1.3 shift

逻辑右移(补0)和算数右移(补符号),C中无符号数的右移是逻辑的,有符号的数是算数右移。如果移位很大,会对数的位数w取模。

3.1.4 overflow

溢出检测程序:

//无符号溢出条件检测证明:
令s = x + y
0 <= x, y < 2^w
当没有产生溢出时,s - x = y >=0, 即 s >= x
当产生溢出时, s - x = x + y - 2^w - x = y - 2^w < 0, 即 s < x
这里x,y的位置可以互换。

int uadd_ok(unsigned int x, unsigned int y) {
    unsigned int s = x + y;
    return s >= x;
}

补码溢出的检测条件
正溢出:x >= 0, y >= 0, 但 x + y < 0
负溢出: x < 0, y < 0, 但 x + y >= 0

int tadd_ok(int x, int y) {
    int z = x + y;

    return !(x >= 0 && y >= 0 && z < 0) && !(x < 0 && y < 0 && z >= 0)
}

3.1.5 C中的余数

C的余数和被除数符号保持一致,如果要余数永远是正值,使用

  • ((n % M) + M) % M

更多的讨论参见C and Python - different behaviour of the modulo (%) operation


四、参考

【1】关于2的补码

【2】深入理解计算机系统 第三版

猜你喜欢

转载自blog.csdn.net/whenloce/article/details/85616333
今日推荐