从数论的角度上探究计算机是如何储存整数与浮点数

计算机存储整数的方法

计算机存储整数主要采用两种方式:无符号整数和有符号整数的存储。每种存储方式都基于二进制系统,但处理正负数的方式不同。

1. 无符号整数的存储

无符号整数只表示非负数。在无符号整数存储中,所有的位(bit)都用于表示数值的大小,没有位被用来表示符号。

存储范围
  • 8位无符号整数:可以存储从0到255的数值。
  • 32位无符号整数:可以存储从0到4,294,967,295的数值。

2. 有符号整数的存储:补码

有符号整数可以表示正数、负数和零。计算机采用“补码”(Two’s complement)格式来存储有符号整数。

补码的定义和计算

补码的计算方式如下:

  • 正数和零:直接使用其标准的二进制表示。
  • 负数
    1. 取其正数的二进制表示。
    2. 将所有位取反(0变为1,1变为0)。
    3. 最后结果加一。

3.通过数论来推导整数的储存规则

对于0-255的每一个数 Xi , Xi mod 256 得到的余数与 另一个数 Xj mod 256 得到的余数都不相同

对于0-255 数论上定义这是 mod 256 的一个最小非负完全剩余系 {0,1,2…254,255}

实际上计算机储存的8 Bit数据就是 2^8的一个完全剩余系

  • 如果我们储存的整数是无符号的,那么我们的储存范围就是0-255,储存的是mod 256 的一个最小非负完全剩余系 {0,1,2…254,255}

  • 如果我们储存的整数是有符号的,那么我们储存范围就是 -128-127,储存的是mod 256 的一个剩余系 {-128,-127…126,127}

如何转化负数?

  • -128 -127 -126 -125 为例

我们有同余方程 -128=128 mod 256

====> Dec (128)=Bin (1000 000)

我们有同余方程 -127=129 mod 256

====> Dec (129)=Bin (1000 0001)

我们有同余方程 -126=130 mod 256

====> Dec (130)=Bin (1000 0010)

  • 这里我们和 127 126 125 做对比

====> Dec (127)=Bin (0111 1111)

====> Dec (126)=Bin (0111 1110)

====> Dec (125)=Bin (0111 1101)

这里我们注意到储存有符号整数的剩余系中,对于负数的储存都是转化为同余数之后在储存

这帮发明计算机的大爹们就注意到负数的首位都是1,正数的首位都是0
于是这个首位就被定义为了符号位

示例
  • -1的补码:在8位系统中,因为 (-1 \equiv 255 \mod 256),因此-1的补码就是255的二进制表示11111111
  • -127的补码:(-127 \equiv 129 \mod 256),因此-127的补码是129的二进制表示10000001

计算机储存浮点数规则

1. IEEE754浮点数标准

  • 单精度float:32位bit(四字节)储存, 符号S位占1bit, 指数E占8bit, 尾数M占23bit

  • 双精度double:64位bit(八字节)储存, 符号S位占1bit, 指数E占11bit, 尾数M占52bit

2. 指数 E 是什么?

指数 E 是个无符号整数,表示 float 时,一共占 8 bit,所以它的取值范围为 0 ~ 255。

但因为指数可以是负的,所以规定在存入 E 时在它原本的值加上一个中间数 127(2^7-1),这样 E 的取值范围为 -127 ~ 128。

表示 double 时,一共占 11 bit,存入 E 时加上中间数 1023,这样取值范围为 -1023 ~ 1024。

3. 为什么指数要加127?

这里储存指数位的数学原理还是数论中的剩余系

这里还是以 8 bit 为例子

如果指数位E只是正数的话 0000 0000 就可以储存 0 - 255

比如:

Dec (127)=Bin ( 0111 1111)

但是对于浮点数来说指数位也是存在负数的

比如储存 Dec (0.5)=Bin (0.1) = Bin (1x2^-1)

E = -1

对于负整数的储存,我们采取补码的形式来解决(即对剩余系中的负数 +256进行映射)。

补码中我们使用符号位来记录数值的正负。

想一下我们补码的符号位是怎么来的

在补码中我们采取对剩余系中负数+256进行映射

{-128,-127…127} 映射到 {0,1,2…254,255}

这就导致了负数的首位都是1,而正数的首位都是0。

但是在浮点数的储存中,我们已经有一个符号位来记录了。

为了避免符号位的重复,我们选择对剩余系中的每一个数字都进行映射

在标准的float储存中,指数位是 8 Bit (0000 0000)

于是我们的映射就是对每一个数都 +127 进行同余映射

{-127…128} 映射到 {0,1,2…254,255}

与补码映射的对比

  • 补码映射:对含负数的剩余系中的负数+256进行映射

{-128,-127…127} 映射到 {0,1,2…254,255}

0 -----> 0
127 ----> 127
-128 —> 128

  • 指数位映射:对含负数的剩余系中的每一个+127进行映射

{-127…128} 映射到 {0,1,2…254,255}

0 -------->128
-127 ------>0
-10 ------>117

这里映射选取的值取决于剩余系中的最小负数

4. 指数位与尾数位的特殊值

  • 指数 E 非全 0 且非全 1:规格化数字,按上面的规则正常计算
  • 指数 E 全 0,尾数非 0:非规格化数,尾数隐藏位不再是 1,而是 0(M = 0.xxxxx),这样可以表示 0 和很小的数
  • 指数 E 全 1,尾数全 0:正无穷大/负无穷大(正负取决于 S 符号位)
  • 指数 E 全 1,尾数非 0:NaN (Not a Number)

这个特殊值的规定导致了float的 E的实际取值范围不是-127-128,而是-126-127

因为-127映射之后是0,表示为0000 0000 全0特殊值
因为128映射之后是255,表示为1111 1111 全1 特殊值

5. 浮点数的储存范围

这里以float类型为例

上面我们得知float类型的指数E取值范围是[-126,127],尾数的最大值为1.111…(即小数点后23位)

所以float类型的规格最大储存值是

(1+(1−2−23))×2127 ≈3.4028235×10^38

同理也可以得到规格最小储存值是

(0.000…1)×2^-126 ≈2-23x2-126 =2^-149 ≈1.40129846×10^−45

6.浮点数为什么会有精度损失

如果我们现在想用浮点数表示 0.2,它在转化为二进制的时候会发生尾数的截断

0.2 转换为二进制数的过程为,不断乘以 2,直到不存在小数为止,在这个计算过程中,得到的整数部分从上到下排列就是二进制的结果。

尾数位的限制
因为float类型只有23位尾数位,而double类型有52位尾数位,这意味着在达到这些位数后,剩余的位数将被截断。这个截断就是精度损失的来源。即使是使用double类型,虽然精度更高,但如果数字的二进制表示超过52位,仍然会有精度损失。

在float中最多会转化 23 bit 二进制小数
在double中最多会转化 52 bit 二进制小数

因此,当你在编程中使用浮点数时,可能会注意到例如0.2 + 0.1不精确等于0.3,而是一个非常接近0.3但略有不同的数。这种现象是由于二进制表示中的截断和舍入误差造成的。

  • 13
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我我我想出去玩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值