IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> [C/C++]数的存储和表示 -> 正文阅读

[C++知识库][C/C++]数的存储和表示

1 整数的表示

在C/C++中,整数的表示有两种,一种是无符号的(unsigned integer,简写为U),另一种是有符号的(用Two’s-complement encodings表示,简写为T)

1.1 无符号数

这玩意儿应该是C/C++中特有的,java和python等是没有这种数据格式的。我们假设有一个m位的二进制数,如果这些二进制数是无符号数的编码,那么这些二进制数对应的无符号数(10进制)为

B 2 U w ( x ) = ∑ i = 0 ( w ? 1 ) x i 2 i B2U_w(x)=\sum ^{(w-1)}_{i=0}x_i2^i B2Uw?x=i=0(w?1)?xi?2i
如x=1011,则B2U(1011)=11。

从上面式子可知,w位的无符号数的最大值和最小值分别是2^w-1,0

1.2 符号数

这是我们通常意义下的整数,它包含正数和负数。他的编码要求是将最高位当做符号位(负数位),其余位为正数。同样假设有一个m为二进制数,这个二进制数是符号数的编码(这个编码方式叫two’s-complement encodings),则对应的符号数(10进制)为:

B 2 T w ( x ) = ? x w ? 1 2 w ? 1 + ∑ i = 0 ( w ? 2 ) x i 2 i B2T_w(x)=-x_{w-1}2^{w-1}+\sum ^{(w-2)}_{i=0}x_i2^i B2Tw?x=?xw?1?2w?1+i=0(w?2)?xi?2i

如x=0011,则B2U(x)=B2T(x)=3;x=1011,则B2U(1011)=11,B2T(1011)=-5。

由上式可以推导,w位的符号数最大值为
∑ i = 0 ( w ? 2 ) x i 2 i \sum ^{(w-2)}_{i=0}x_i2^i i=0(w?2)?xi?2i

最小值为
? x w ? 1 2 w ? 1 -x_{w-1}2^{w-1} ?xw?1?2w?1

此外,若想要让任意的正数变为对应的负数,那么可以先取该正数的two’s-complement encodings,设为T,则(~T)+1就为所求,如: 7 = 0111,(~7)+1 = 1000+1 = 1001 = -7。(注意在4bits的情况下,最大值只是7,请不要取11这种数字)。同样的你要让-7变为7也可以用(~-7)+1来获得。实际上这就是我们通常说的补码形式。

符号数需要注意的地方是,在4bits情况下,最大值是7,如果相加两个最大值得到的不是14,而是-2。

1.3 无符号数和符号数的转换

假设我们有w位二进制数,则两者的转换为:
T 2 U w ( x ) = x + x w ? 1 2 w T2U_w(x)=x+x_{w-1}2^w T2Uw?(x)=x+xw?1?2w
U 2 T w ( u ) = ? u w ? 1 2 w + u U2T_w(u)=-u_{w-1}2^w+u U2Tw?(u)=?uw?1?2w+u

上式的推导基于
B 2 U ? B 2 T = x w ? 1 2 w B2U-B2T =x_{w-1}2^w B2U?B2T=xw?1?2w

我们考虑一个4bits的数,T2U(-7) = -7+16 = 9;这就好像把负数进行翻转成正数一样。
U2T(9) = 9-16 = -7

1.4 补全(extension)和截断(truncating numbers)

1.4.1 无符号的补全

假设我们的机子是一个8bits机子(或者说编译器是处理8bits的),那么我之前的例子中的4bits这个数如果仍要放到这个机子里则必须把他转化为8bits的数。对于无符号数而言可将其拆分为:
B 2 U w ( x ) = ∑ i = h ( w ? 1 ) x i 2 i + ∑ i = 0 ( h ? 1 ) x i 2 i B2U_w(x)=\sum ^{(w-1)}_{i=h}x_i2^i+\sum ^{(h-1)}_{i=0}x_i2^i B2Uw?x=i=h(w?1)?xi?2i+i=0(h?1)?xi?2i
其中,w是整个的位数,h是我们目前存的数的位数。则我们只要在h为之后全补上0,则上式仍然等于原数。如5 = 0101,拓展后为 0000 0101;

1.4.2 有符号的补全

我们效仿无符号的拆分,则:
B 2 T w ( x ) = ? x w ? 1 2 w ? 1 + ∑ i = h ? 1 ( w ? 2 ) x i 2 i + ∑ i = 0 ( h ? 2 ) x i 2 i B2T_w(x)=-x_{w-1}2^{w-1}+\sum ^{(w-2)}_{i=h-1}x_i2^i+\sum ^{(h-2)}_{i=0}x_i2^i B2Tw?x=?xw?1?2w?1+i=h?1(w?2)?xi?2i+i=0(h?2)?xi?2i
显然若
∑ i = h ? 1 ( w ? 2 ) x i 2 i \sum ^{(w-2)}_{i=h-1}x_i2^i i=h?1(w?2)?xi?2i
x全为1,则上式=原数。如 -7 = 1001,拓展后为 1111 1001 = -7

1.4.3 截断

截断的发生是因为overflow,溢出。而溢出指的是当前的位数无法存放你想要放的数,比如对于w位的符号数而言,他的最小值是
? x w ? 1 2 w ? 1 -x_{w-1}2^{w-1} ?xw?1?2w?1
用两个最小值相加,得到的是
? x w ? 1 2 w -x_{w-1}2^{w} ?xw?1?2w
这意味在二进制下上数左移<<1,如果说目前的“容器”只有w位,那么他就不能存下(w+1)位,因此他只能放弃这个(1)位,而存下w位。比如说4bits下的最小值-8=1000,-8+(-8)=1 0000,但是显然我们存不下第5位,所以-8+(-8) = 0。

另一个例子是“容器”的变更,比如一个int数强转成short数,那么此时也会发生上面说的截断。

假设有一个w位的数,然后放进一个只有k的容器中(w>=k),则无符号数的截断的数学表示如下:

B 2 U w ( x ) m o d ? 2 k = [ ∑ i = 0 ( w ? 1 ) x i 2 i ] m o d ? 2 k B2U_w(x)mod \ 2^k=[\sum ^{(w-1)}_{i=0}x_i2^i] mod \ 2^k B2Uw?xmod?2k=[i=0(w?1)?xi?2i]mod?2k

假设w=k+2的情况,则i=k时,(x2^k) mod 2^k = 0,i=k+1时也是如此,这就意味着超过容器k的位数的其他位,无论是什么都将转化为0。而 i = 2时, x2^2 mod 2^k 保留的就是 x2^2 本身。

至于有符号数,同样的我们需要把超过k的位数全部转化为0,因为不要像补全那样补1,所以可以转化成无符号数进行无符号的处理:

B 2 U w ( x ) ? m o d ? 2 k = U 2 T k ( B 2 U w ( x ) ? m o d ? 2 k ) B2U_w(x)\ mod \ 2^k = U2T_k(B2U_w(x)\ mod \ 2^k) B2Uw?(x)?mod?2k=U2Tk?(B2Uw?(x)?mod?2k)

虽然看起来式子很复杂,但实际上他做的事就是截断一下,比如w=4,k=3,则符号数2截断后是 010 = 2; -7 = 1001, 截断后是 001 = 1

1.5 运算

  • 由于运算实际上仍然是涉及补全和截断的内容,所以这里不再写具体的操作和公式了,但仍然需要说明的是,因为截断的缘故,无论是无符号的书还是有符号的数,他们的相加永远只能在某个范围内,从而实现一种循环。此外,符号数中,两个正数的相加未必是正数,两个负数的相加未必是负数,因为他们二进制编码的关系以及截断的关系。

  • 一个有用的点是乘法,因为历史缘故,以前的机器对于乘法处理不太行,所以很多时候乘法的处理都变成左移右移的操作,比如说:
    x × 14 = x ( 2 3 + 2 2 + 2 1 ) = ( x < < 3 ) + ( x < < 2 ) + ( x < < 1 ) x\times 14 = x(2^3+2^2+2^1) = (x<<3)+(x<<2)+(x<<1) x×14=x(23+22+21)=(x<<3)+(x<<2)+(x<<1)
    x × 14 = x ( 2 4 ? 2 1 ) = ( x < < 4 ) ? ( x < < 1 ) x\times 14 =x(2^4-2^1) = (x<<4)-(x<<1) x×14=x(24?21)=(x<<4)?(x<<1)

  • 除法的操作与乘法类似,但是因为他会存在小数问题,此时在用正数表示时就会有rounding问题,如果是正数则rounding down,如果是负数则rounding up。但是C好像在这方面的处理是只能处理rounding down,他内部的rounding up的操作是用(x+y-1)/y的roungding down实现的。

2 浮点数,IEEE 754标准

2.1 表示

早些时候浮点型数字的表示是五花八门的直到IEEE 754标准,对于一个可能的小数(注意不是任意),他的表示是:

V 进 制 = ( ? 1 ) s × M 进 制 × 2 E V_{进制} = (-1)^s\times M_{进制}\times 2^E V?=(?1)s×M?×2E
因此保存一个浮点数我们只需要存储s,M,E这三个数据。对于这3个数的存贮,他们有一个统一的样式
在这里插入图片描述
对于C语言的单精度而言,存e的地方又8bits,存f的地方有23bits。

在上面的统一样式下s,M,E的具体存储又有三种类型(他是根据e的情况来分的),但在具体说类型前,需要说一个数字bias,他等于2^(k-1)-1。
(以下全以正数为例)

  • 第一种,若e全为0,则:E=1-bias,M=f。这种通常表示接近0的浮点数。
  • 第二种,若e不全为0 && 也不全为1,则:E=e-bias, M=f+1。这种表示一般意义下的浮点数。
  • 第三种,若e全为1,则:1.若 f全为0,则该数V是无穷数;2. 若f不全为0,则V=NaN,不是数。

假设我们是一个8bits的系统,设k=4,n=3,则考虑以下例子:

Bit(s e f)eEfMV十进制二进制
0 0000 0000-60/80/800.00.0
0 0000 1110-67/87/87/5120.0136720.111*2^(-6)
0 1110 1111477/815/81920/8240.01.111*2^7
0 1111 000-----正无穷正无穷
0 1111 101-----NaNNaN

这个表有两个地方值得说的:

  • 如果我们希望转换240这个数变成240.0这种浮点数,那么240=1111 0000=1.111*2^7,而这个就正是0 1110 111的表示。
  • 在确定了e是哪种模式后,在确定E后,我们可以直接写出V的二进制形式,而无需像十进制那样进行转换。

十进制下1/3=0.333333…,我们无法用有限位的小数去表示这样的数,所以我们可能用0.333≈1/3来处理他,所以我们会有精度损失的问题。所以同样的,我们在2进制下也有这种问题,特别是电脑的位数都是固定的情况下。比如说1/10=0.00011001100110011(0011)…,因为电脑只能存有限位,假设只能存23bits,x=0.00011001100110011001100,则会产生误差0.1-x=0.00000000000000000000001100(1100)…。这种误差如果是通过某种方法累计的则会产生由精度带来的问题。

2.2 rounding

对于rounding,我们通常说4舍5入,但是这可能会带来一些统计上的问题,因为1-4是4个数,而5到9是5个数。而这个统计问题在计算机中是一定会有的,因为计算机的位数是有限的,所以必须要rounding。为了在统计意义上消除这个问题,因此定义10进制下的rounding规则如下:

  • 1-4,round down
  • 5,round to even
  • 6-9,round up
Mode1.41.61.5
round to even122
round down111
round up222

round to even是一个“复合”的操作,他的操作步骤为,先确定这个数的round down, round up,如果你要round的位数小于中间数(5),则取round down,大于5则取round up。如果等于5,则判断round up是even的还是round down是even的。


实际上,我们更关心的是二进制的情况,对于任意二进制浮点数数而言,我们都可以写成xxxxxx.yyyyyyz,这种形式(我为了简便,xxxx可以是任意0,1的组合,不一定全是1或0,y同理),此时也分3种情况:

  • z=1000…,even
  • z>1000…,up
  • z<1000…,down

比如说我需要round 1bit处,则对于10.010来说,我们写成10.0|10,后面的10显然=100…,所以使用even,则去掉10为round down,10.0;去掉10+1,10.1为round up,因为0是even所以用round down=10.0。

原数rounded
10.01010.0
10.01110.1
10.11011.0
11.00111.0

rounding同样会带来类似精度损失的问题,由于分析相同,此处不再赘述。

2.3 运算

对于他们的运算都可以直接转化为
V 二 进 制 = ( ? 1 ) s × M 二 进 制 × 2 E V_{二进制} = (-1)^s\times M_{二进制}\times 2^E V?=(?1)s×M?×2E
然后相加或相乘之类的再表示为10进制数,但需要注意的是,浮点数的运算只能支持“可交换律”,x+y=y+x,但是对于“结合律”是有问题的,如x+y+z!=x+(y + z),而在乘法中,分配律也是有问题的。因此如下面的计算b+c+a+d

t = b+c;
x = a+t;
y = t+d;

这种方法运用了结合律,是有问题的。但是在实际上面来看,虽然带来了问题,但是这种问题通常是小的(?),所以得到的数据还算能用(?)。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-07-04 22:37:15  更:2022-07-04 22:37:47 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 16:50:35-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码