CSAPP——数据在计算机内部的表示(整型与浮点型)

一.数据类型

  在冯偌依曼体系结构中,程序代码和数据都是以二进制存储的,对计算机系统和硬件本身而言,数据类型的概念并不存在,任何数据在计算机内部都是0/1序列。这里讲的数据类型是以C语言为基础的,下面的这张图概括了C语言中主要的数据类型。

  那么对于一串0/1序列 我们怎么知道该数值数据代表的值是多少呢?所以为了确定一个数值数据,我们必须确定数值数据表示的三要素:

  1. –进位计数制
  2. –定、浮点表示
    (解决小数点问题)
  3. 如何用二进制编码
    (解决正负号问题)

  下面主要来讲一讲整型数据和浮点数据的相关表示。

二.整型

1.类别

整型数据分为:无符号整数带符号整数
有符号数和无符号数的区别主要在于有没有最高位的符号位,以及由此带来的计算方式的不同。符号位中,0 表示非负数,1 表示负数。
32位机器上long int 为4个字节,而在64位机器上,long int为8个字节
在这里插入图片描述

2.表示范围

在这里插入图片描述
可以发现,带符号数的最大值TMax = 2^(w-1)-1 (w为位数)与最小值TMin = $-2^(w-1), 这两个数的绝对值并不相等,为了弄清楚这个问题,我们必须知道定点数的编码表示。

3.定点数的编码表示方式

1.原码

符号位:正号0表示,负号1表示,其它数字位代表数值本身的绝对值。
采用原码表示,虽然容易理解,但是会带来很多麻烦:

  • 0的表示不唯一,不利于程序员编程;
  • 加、减运算方式不;
  • 需要额外对符号位进行处理,不利于硬件设计。

所以,从50年代开始,整数都采用补码表示。浮点数的尾数还是用原码定点小数表示(下面相应部分将会讲到)

2.反码

  如果是正数,则表示方法和原码一样;如果是负数,符号位不变,其余各位取反,则得到这个数字的反码表示形式。(反码很少用到)

3.补码-模运算

(1)如何求得补码

    如果是正数,则表示方法和原码一样;如果是负数,则将数字的反码加上1即得到它的补码;

    举个例子:  为了简便,这里只定义位数w=4。

  • -5的原码是1101;

  • 将除符号位的其余位置取反得到反码1010;

  • 末尾加1,得补码:1011

(2)补码与真值的关系

当我们知道一个数的补码,要求它的真值,可以采用以下两种方式:

  • (1)看符号位! 若符号位为0,则为正数,其值就是后面的数值部分的值;若符号位为1,则为负数,将数值位各位取反,再加1,取结果为负值。
        举例:0110,其值为0 * 1+1 * 2+1*4=6;
                  1110值就等于将110取反再加1的值,即:110---->001---->010,所以其值为-2。

  • (2)公式:
    因为能力有限,只能在word里打出来这个公式,且不能复制过来,只好截图了。
    解释一下:w是这个数据的位数,k是从0开始计数的序数,如:第一位的k=0,第二位的k=1,以此类推…
    在这里插入图片描述
    对于上面的补码1110=- 2^3+(1 * 2+1*4) = -2

有了这个公式,就可以解开上面提到的那个问题:最大值比最小值的绝对值小1。

(3)模运算概念:

  在一个模运算系统中,一个数与它除以“模”后的余数等价!

    举个例子:时钟就是一种模12系统。如果现在是时针指向8点,要将它拨向6点,则有两种拨法:

  • 倒拨2格:8-2=6
  • 顺拨10格: 8+10=16; (mod 12)

模12系统中:8-2=8+10   (mod 12)
                     -2=10            (mod 12)
则称-2是10对模12的补码。

4.有无符号数的转换

转换公式:当TMin< T<0时:U=2^w+T; (T为补码)在这里插入图片描述

注意点: 如果一个表达式既包含有符号数也包含无符号数,那么会被隐式转换成无符号数进行比较 s = U A d d w ( u , v ) = u + v    m o d    2 w s=UAdd_w(u,v)=u+v \; mod \; 2^w s=UAddw(u,v)=u+vmod2w

三.浮点数

1.IEEE754标准

在 IEEE 标准中,我们用下面的公式来表达浮点数:

( − 1 ) s    M    2 E (-1)^s \; M \; 2^E (1)sM2E
其中 s 是符号位,决定正负;M 通常是一个值在 [1.0, 2.0) 的小数;E 是次方数。具体编码时结构如下,这里用单精度、双精度和扩展精度为例:在这里插入图片描述
其中 s 对应着符号位,exp 对应着 E(注意,不一定等于 E,因为位数限制表达能力有限),frac 对应着 M(注意,不一定等于 M,因为位数限制表达能力有限)。不同的位数就代表了不同的表示能力,也就是单精度,双精度,扩展精度的来源。

规范化值(Normalized Values)

这里的 E 是一个偏移的值 E = E x p − B i a s E=Exp-Bias E=ExpBias,其中
Exp: 是 exp 编码区域的无符号数值
Bias:值为 2 w − 1 − 1 2^{w-1} - 1 2w11 的偏移量,其中w 是 exp 编码的位数,也就是说
单精度:127(Exp: 1…254, E: -126…127)
双精度:1023(Exp: 1…2046, E: -1022…1023)
之所以需要采用一个偏移量,是为了保证 exp 编码只需要以无符号数来处理。

而对于 M,一定是以 1 开头的:也就是 M = 1. x x x … x 2 M=1.xxx…x_2 M=1.xxxx2。其中 xxx 的部分就是 frac 的编码部分,当 frac=000.00 的时候值最小( M = 1.0 M=1.0 M=1.0),当 frac=111。。。1 的时候值最大( M = 2.0 − ϵ M=2.0-\epsilon M=2.0ϵ),也就是说开头的 1 是免费附送的,并不需要实际的编码位。

举个例子,float F = 15213.0;,那么

1521 3 10 = 1110110110110 1 2 = 1.110110110110 1 2 × 2 13 15213_{10}=11101101101101_2=1.1101101101101_2 \times 2^{13} 1521310=111011011011012=1.11011011011012×213

于是 frac 部分的值就是小数点后面的数值,而 Exp = E + Bias = 13 + 127 = 140 = 1000110 0 2 10001100_2 100011002,于是编码出来的浮点数是这样的:
                             0 10001100 11011011011010000000000

非规范化值(Denormalized Values)

e x p = 000 … 0 exp = 000…0 exp=0000 的时候,值是非规范化的,意思是,虽然实数轴上原来连续的值会被规范到有限的定值上,但是并些定值之间的间距也是一样的。

v = ( − 1 ) s    M    2 E v=(-1)^s \; M \; 2^E v=(1)sM2E

和前面不同的是

E = 1 − B i a s E = 1 - Bias E=1Bias

而且 M = 0. x x x … x 2 M=0.xxx…x_2 M=0.xxxx2,不是以 1 开头了。

当 exp=000…0 且 frac = 000…0 时,表示 0,而且因为符号位的缘故,实际上是有 +0 和 -0 两种的。而在 exp=000…0 且 f r a c ≠ 000 … 0 frac \ne 000…0 frac=0000 时,数值是接近 0 的,并且间距是一致的

特殊值

还有一种特殊情况,就是 e x p = 111 … 1 exp = 111…1 exp=1111 时,表示一些特殊值。

当 exp=111…1 且 frac = 000…0 时,表示 ∞ \infty ,而且因为符号位的缘故,实际上是有 + ∞ +\infty + − ∞ -\infty 两种的。那些会溢出的操作就会用这个来表示,比如 1.0 / 0.0 = − 1.0 / 0.0 = + ∞    ,    1.0 / − 0.0 = − ∞ 1.0/0.0=-1.0/0.0=+\infty\;,\;1.0/-0.0=-\infty 1.0/0.0=1.0/0.0=+,1.0/0.0=

而在 exp=111…1 且 f r a c ≠ 000 … 0 frac \ne 000…0 frac=0000 时,我们认为这不是一个数值(Not-a-Number,NaN),用来表示那些没办法确定的值,比如 s q r t ( − 1 ) , ∞ − ∞ , ∞ × 0 sqrt(-1),\infty-\infty,\infty\times 0 sqrt(1),,×0

2.浮点数精度

浮点数精度主要是由尾数部分决定的,有效的位数部分越长,其精度越高(单精度浮点数尾数部分部分最高为23位,双精度浮点数最高为51位)。一个0/1序列的浮点数可以准确对应一个小数,而一个小数却不一定能表示成0/1序列
61.419998和61.420002是两个可表示数,两者相差0.000004,这之间的数是不可表示的,当输入的数据是一个不可表示数时,机器数将其转换为最邻近的可表示数。
看下面的代码:

#include <stdio.h>
int main()
{
	float a;
	printf("请输入:\n");
	scanf("%f",&a);
	printf("x=%f",a);
	return 0;
}

测试
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

参考资料、网站:

  • https://wdxtub.com/csapp/thin-csapp-1/2016/04/16/(不周山作品集)
  • https://www.icourse163.org/learn/NJU-1001625001?tid=1205931209#/learn/content?type=detail&id=1210291298&cid=1212230667(南京大学开设的“计算机系统基础”系列课程)
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值