硬件结构——(7) 为什么 0.1 + 0.2 不等于 0.3?

1. 为什么负数要用补码表示?

⼗进制数转⼆进制 采用除 2 取余法。例如:数字8转⼆进制的过程如下图:

整数类型的数字 在计算机中的存储方式,就是将十进制数 转换成⼆进制。

以int 类型的数字为例,int类型大小为32位,其中最高位作为 "符号标志位" (正数的符号位是0,负数的符号位是1),剩余的31位 表示二进制数据。例如:int类型的数字1 的⼆进制数表示如下图:

负数在计算机中 用"补码"表示,补码就是将正数的二进制 全部取反再加1。例如:int类型的数字-1 的⼆进制数表示如下图:

问题:为什么计算机要用补码的⽅式来表示负数?

先假设不用补码的方式 来表示负数,而只是将最高位的符号标志位变为1 来表示负数,如下图:

-2+1 应该等于 -1,但上面的运算结果却是-3。所以这种负数的表示方式 不能用常规的加法来计算,因此需要先进行特殊处理。要先判断数字 是否为负数,如果是负数 就要将加法操作变为减法操作 才能得到正确的结果。
如果负数不使用 补码的方式表示,则做基本的加减法运算时,还需要多一步操作 来判断是否为负数。如果为负数,则需要将加法操作变为减法操作 或者将减法操作变为加法操作,这样效率将会很低,因为加减法运算在计算机中的使用 是非常频繁的,所以应该尽量 简化运算过程。
如果负数使用 补码的方式表示,则负数的加减法操作 实际上与正数的加减法操作 是一样的。例如:如果负数用补码的方式表示,则运算-2+1的过程如下图:

2. 十进制小数与二进制的转换

已经知道了 整数十进制转⼆进制,小数部分的转换 采用的是乘 2 取整法。例如:数字8.625转⼆进制的过程如下图:

最后将 "整数部分" 和 "小数部分" 结合在⼀起,其结果就是1000.101。

但是,并不是所有小数都可以用二进制表示。例如:数字0.1转⼆进制的过程如下图:

可以发现,0.1的⼆进制表示 是无限循环的。由于计算机的资源是有限的,所以只能用 "近似值" 来表示0.1 (即 在有限的精度条件下,最接近0.1的二进制数),因此会造成 精度的缺失

注:二进制小数转十进制时,小数点后的指数幂 是负数。比如:二进制1010.101转十进制的过程如下图:

3. 计算机是怎么存小数的?

⼆进制小数1000.101 为"定点数形式" (即小数点是定死的,不能移动,如果小数点移动 则原数值就会被改变)。然而,计算机存储小数采用的是 "浮点数形式" (即小数点是浮动的)。

示例:1000.101这个⼆进制数,可以表示成 1.000101 x 2^3,类似于数学上的科学记数法。
注:如果⼆进制使用科学记数法,则需要规范化。不仅要保证 基数为2,还要保证 小数点左侧只有1位,并且必须为1
所以⼆进制数1000.101 规格化表示成1.000101 x 2^3。其中:

  • 000101 为尾数部分,即小数点后面的数字;
  • 3 为指数部分,指定了小数点在原数据中的位置;

现在的绝大多数计算机 使用的浮点数,⼀般采用的是 IEEE 制定的国际标准。这种标准形式如下图:

  • 符号位:0 表示正数, 1 表示负数;
  • 指数位:指定了小数点在原数据中的位置,指数可以是负数,也可以是正数,指数位的长度越长 则数值的表达范围就越大;
  • 尾数位:小数点右侧的数字,即小数部分 (比如:二进制1.0011 x 2^(-2)的尾数部分 就是0011),尾数的长度决定了 这个数的精度,因此如果想要表示 精度更高的小数,就要提高 尾数位的长度;

32位来表示的浮点数 称为单精度浮点数 (float);用64位来表示的浮点数 称为双精度浮点数 (double)。它们的结构如下图:

  • double 的尾数部分是 52位,float 的尾数部分是 23位,由于同时都带有⼀个 固定隐含位(小数点左边的整数1),所以double有 53个⼆进制有效位、float有 24个⼆进制有效位。double的精度用十进制表示为 log10(2^53) 约等于 15.95,float的精度用十进制表示为 log10(2^24) 约等于 7.22。因此,double的有效数字是 15~16位,float的有效数字是 7~8位,这些有效位包含了 整数部分和小数部分
  • double的指数部分是 11位,float的指数位是 8位,意味着double相比float 能表示更大的数值范围。

问题:⼆进制小数是如何转换成 ⼆进制浮点数的?例如:float类型的数10.625 的存储方式如下图:

移动后的二进制浮点数1.010101 x 2^3 的小数点左侧的有效位1 没有被存储。因为根据IEEE标准规定:⼆进制浮点数的小数点左侧 只能有1位,且该位上只能是1。所以既然这⼀位 永远都是1,就不需要存起来了。因此,23位尾数只存储小数部分,可以节约1位的空间,尾数就能多存一位小数,精度就更高一点
 

float的⼆进制浮点数 转换成十进制时,要考虑到这个隐含的1。转换公式如下图:

举例:将下图中的float 转换成十进制:

4. 0.1 + 0.2 == 0.3 ?

0.1 的float浮点数:

可以看到,8位指数部分是 01111011,23位的尾数部分是 10011001100110011001101。尾数部分中有 0011循环,但由于尾数是有 长度限制的,所以最终只会显示一个近似

0.2 的float浮点数:

可以看到,8位指数部分是 01111100 ,23位的尾数部分是10011001100110011001101,尾数部分和0.1相同,也是⼀个近似值。

0.1⼆进制浮点数 转换成十进制的结果是 0.100000001490116119384765625 :

0.2的⼆进制浮点数 转换成十进制的结果是 0.20000000298023223876953125 :

这两个数 相加的结果是 0.300000004470348358154296875 :

可以看到,在计算机中 0.1 + 0.2 并不等于完整的 0.3。这是因为 有的小数无法用 "完整" 的二进制来表示,所以计算机只能采用 近似数的方式来保存,近似数相加的结果 也是一个近似数

二进制只能精准表达 2除尽的数字。如:1/2、1/4、1/8;但对于0.1(1/10)、0.2(1/5),则在⼆进制中 无法精确表示,因此需要 根据精度舍入。(十进制也有无法除尽的数,如:1/3、1/7 也需要根据精度舍入)
 

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据相位稳定的定义,我们需要找到一个频率 Wcp,使得相位满足 -ψ = -180°,即 ψ = 180°。此时系统的相位裕度为 0°,系统处于边缘稳定状态。 首先,我们需要将 W(p) 表示成极点和零点的形式。将分母和分子分别因式分解,得到: W(p) = 30 • (0.1p+1) • (12.5p+1) / [p • (10p+1) • (0.2p+1) • (p+1)] = 375p/(p+1) - 3750/(10p+1) + 750p/(0.2p+1) - 3750p/(10p+1) + 150p/(p+1) + 30 因此,系统的极点为 -1、-0.1、-0.2、-0.05、-0.01,零点为 0。 接下来,我们需要找到系统的幅频特性和相频特性。计算幅频特性时,我们可以将所有极点和零点的贡献相加。计算相频特性时,我们需要分别计算每个极点和零点的相位贡献,然后将它们相加。 幅频特性: G(jω) = 30 • (jω+0.1) • (jω/12.5+1) / [jω • (jω/10+1) • (jω/0.2+1) • (jω+1)] 将 G(jω) 化简,得到: G(jω) = -0.15 • (jω+0.1) / (jω+1) + 1.5 • (jω/10+1) / (jω+1) - 3 • (jω/0.2+1) / (jω+1) + 1.5 • (jω/10+1) / (jω+1) - 0.3 • (jω+1) / (jω+1) + 30/(jω+1) 相频特性: φ(jω) = φ(0) + Σ(φi - φj),其中 i 表示极点,j 表示零点 φ(jω) = arctan(ω/0.1) + arctan(12.5ω) - arctan(10ω) - arctan(0.2ω) - arctan(ω) + arctan(0) 现在我们可以画出系统的幅频特性和相频特性,如下图所示(红色为幅频特性,蓝色为相频特性): ![image.png](attachment:image.png) 从图中可以看出,系统的相位在 0.9rad/s 处通过 -180°。因此,我们可以得到: Wcp = 0.9rad/s ψ = 180° 答案:Wcp = 0.9rad/s,ψ = 180°。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值