三、计算机运行小数出错原因

216 阅读5分钟

二进制数表示整数

二进制转换整数

跟处理整数一样, 各位数值与位权相乘再相加即可


小数点后面部分的位权,第 1 位是 2 的-1 次幂、第 2 位是 2 的-2 次幂

比如1011.0011, 转换成十进制
image.png

整数转换二进制数

十进制小数转换成二进制小数采用"乘2取整,顺序排列"法。

具体做法是:用2乘十进制小数,可以得到积,将积的整数部分取出,再用2乘余下的小数部分,又得到一个积,再将积的整数部分取出,如此进行,直到积中的小数部分为零,此时0或1为二进制的最后一位。或者达到所要求的精度为止。

如:0.625=(0.101)B
0.625*2=1.25======取出整数部分1
0.25*2=0.5========取出整数部分0
0.5*2=1==========取出整数部分1

计算机运行出错的原因

有一些十进制小数无法转换成二进制小数


例如: 小 数 点 后 4 位 用 二 进 制 数 表 示 时 的 数 值 范 围 为 0.0000~0.1111。因此,这里只能表示 0.5、0.25、0.125、0.0625 这四个 二进制数小数点后面的位权组合而成(相加总和)的小数。将这些数值 组合后能够表示的数值,即为表 3-1 中所示的无序的十进制数
image.png
我们根据十进制小数转换二进制小数规则中,当在转换过程中 * 2后的积的小数部分为0时,转换才会中止,所以只有十进制小数A * 2^n => B,B为整数才可以转换成精确的二进制值

所以 0.1, 0.2, 0.3...这样的值是不可能转换成精确的值的

浮点数

浮点数是指用符号、尾数、基数和指数这四部分来表示的小数。因为计算机内部使用的是二进制数,所以基数自然就是 2


浮点数分为双精度浮点数和单精度浮点数. 双精度浮点数用64位, 单精度浮点数用32位表示全体小数.
image.png
image.png

符号部分: 指用一个数据位来表示数值的符号, 1表示负, 0表示正或0

尾数部分: 用将小数点前面的值固定位1的正则表达式

指数部分: 用Excess表现

正则表达式和Excess系统

正则表达式

按照特定的规则来表示数据的形式即为正则表达式。在二进制数中,我们使用**"将小数点前面的值固定为1的正则表达式"**.


具体来讲, 就是将二进制数表示对小数左移或右移(指逻辑移位, 因为符号是独立的)数次后, 整数部分的第一位变为1, 第2位之后都为0,

image.png

Excess系统

通过将指数部分表示范围的中间值设为 0,使得负数不 需要用符号来表示


当指数部分是 8 位单精度浮点数时, 最大值 11111111 = 255 的 1/2,即 01111111 = 127(小数部分舍弃)表示 的是 0,指数部分是 11 位双精度浮点数时,11111111111 = 2047 的 1/2, 即 01111111111 = 1023(小数部分舍弃)表示的是 0。
image.png
我们以十进制的0.75为例,转换为二进制的浮点数

  1. 将0.75转换成二进制数 => 0.11
  2. 由于是正数, 确定符号位为 0
  3. 确定尾数部分, 0.11 左移 => 1.1 , 补齐23位 => 1.100 0000 0000 0000 0000 0000
  4. 确定指数部分, 由于左移一位, 所以需要2^-1与移位抵消, 指数部分为 -1, 在Excess系统中为127 -1 = 126, 二进制数为 => 01111110
  5. 所以最终为0-01111110-100 0000 0000 0000 0000 0000

image.png
我们再将其转换成十进制数进行验算.

尾数部分100 0000 0000 0000 0000 0000实际上为1.100 0000 0000 0000 0000 0000
所以十进制数为1.5, 而指数部分为-1,即2, 所以 1.5 * 2 = 0.75

如何避免计算机计算出错

计算机计算出错的原因之一是,采用浮点数来处理小数(另外,也 有因“位溢出”而造成计算错误的情况)

  • 首先回避策略,在精确度要求不是很高的场景下,允许误差存在
  • 将小数转换为整数计算
  • 使用BCD(Binary Coded Decimal) 也是一种使用二进制表 示十进制的方法。


补: Java的BigDecimal类通过将浮点数转化为整数计算保证精确度的
BigDecimal的浮点数运算能保证精度的原理是什么?

二进制数和十六进制数

在以位为单位表示数据时,使用二进制数很方便,但如果位数太多,看起来就比较麻烦, 因此,在实际程序中,也经常会用十六进制数来代替二进制数。

二进制数的4位,正好相当于十六进制数的1位。在 C 语言程序中,只需在数值的开头加上 0x(0 和 x)就可以表示十六进制数。

例如: 32 位二进制数00111101110011001100110011001101 用十六进制数来表示的话,就是 3DCCCCCD 这个 8 位数, 所以通过使用十六进制数, 二进制数的位数能至少缩为原来的1/4,看起来更清晰

例如,1011.011 的低位补 0 后为 1011.0110,这时就可以表示 为十六进制数 B.6。十六进制数的小数点后第 1 位的位权是 16, 即 1/16 = 0.0625,

image.png


参考资料: