定点数
定义
定点数是一种实数数据类型,要求小数点后位数固定,有时也要求小数点前位数固定。定点数与更复杂的浮点数相对。 如果程序需要在没有FPU(Floating Point Unit,浮点运算器)的处理器上执行,或者可以通过定点数提高效率和计算精度时,常使用定点数表示基数为2或10的分数。
定点数类型的值其实就是个整数,需要额外做比例进位,进多少位需要根据具体的定点数类型决定。并且相同类型的定点数中所有值的缩放系数都是一致的,在计算过程中也保持不变。
缩放系数通常是 10 或 2 的幂,前者方便人类读写,后者易于高效计算。不过有时也会使用其它比例,例如可以用 1/3600 的比例的定点数来表示以小时为单位的时间值,可以精确到秒。定点数的最大值,可以通过将其内部所使用的整数的最大值乘以缩放系数求得,最小值同理。
常见的定点数类型分二进制和十进制两种。十进制定点数的缩放系数是10的幂,二进制定点数的缩放系数是2的幂。最常用的定点数是二进制的,因为按照缩放系数还原的操作可以用快速的移位操作实现。
常见表示法
Q格式是定点数最常见的表示方法。Qm.f:无歧义的 Q 格式,其中m表示整数位的位数,f表示小数位的位数。因为整个字是补码整数,所以符号位长度可以根据其它信息推导而来。例如 Q1.30 表示该数有 1 个整数位、30 个小数位,是 32 位补码整数。定点数通过补码直接进行运算,考虑补码是考虑数的二进制运算的表达形式。也可以先把数逻辑上转成十进制,之后计算结果,再按定点表示,其中整数和0的补码,是符号位为0,其补码就是本身,而负数的补码是负数按位取反再加1。做定点数乘法的时候,首先忽略掉小数点位置,将两个乘数的补码相乘;然后再确定小数点位置。
type integer bits . fraction bits:
- u1.15
- s7.8
表示的数值范围
表1. Q表示、Qmn表示及数值范围
| Q表示 | Qmn表示 | 十进制数表示范围 |
|---|---|---|
| Q15 | Q0.15 | -1≤x≤0.9999695 |
| Q14 | Q1.14 | -2≤x≤1.9999390 |
| Q13 | Q2.13 | -4≤x≤3.9998779 |
| Q12 | Q3.12 | -8≤x≤7.9997559 |
| Q11 | Q4.11 | -16≤x≤15.9995117 |
| Q10 | Q5.10 | -32≤x≤31.9990234 |
| Q9 | Q6.9 | -64≤x≤63.9980469 |
| Q8 | Q7.8 | -128≤x≤127.9960938 |
| Q7 | Q8.7 | -256≤x≤255.9921875 |
| Q6 | Q9.6 | -512≤x≤511.9804375 |
| Q5 | Q10.5 | -1024≤x≤1023.96875 |
| Q4 | Q11.4 | -2048≤x≤2047.9375 |
| Q3 | Q12.3 | -4096≤x≤4095.875 |
| Q2 | Q13.2 | -8192≤x≤8191.75 |
| Q1 | Q14.1 | -16384≤x≤16383.5 |
| Q0 | Q15.0 | -32768≤x≤32767 |
此类表示方法为并非最标准的表述格式,即有歧义的。Qf格式,Q15 表示 15 个小数位。这样的记法存在歧义,因为没有包含字长。Qm.f:无歧义的 Q 格式,因为整个字是补码整数,所以符号位长度可以根据其它信息推导而来。例如 Q1.30 表示该数有 1 个整数位、30 个小数位,是 32 位补码整数。
Q越大,数值范围越小,但精度越高;相反,Q越小,数值范围越大,但精度就越低。对定点数而言,数值范围与精度是一对矛盾,一个变量要想能够表示比较大的数值范围,必须以牺牲精度为代价;而想精度提高,则数的表示范围就相应地减小。
浮点数相关
浮点数是指数通过科学计数法的形式表示,从而使得数不局限于某种类型的固定小数点位置,从而有更灵活的表达范围,即可以表示的值更大,或者表示的精度更高。
1985年,IEEE 组织推出了浮点数标准,就是我们经常听到的 IEEE754 浮点数标准,这个标准统一了浮点数的表示形式,并提供了 2 种浮点格式:
- 单精度浮点数 float:32 位,符号位 S 占 1 bit,指数 E 占 8 bit,尾数 M 占 23 bit
- 双精度浮点数 float:64 位,符号位 S 占 1 bit,指数 E 占 11 bit,尾数 M 占 52 bit
为了使其表示的数字范围、精度最大化,浮点数标准还对指数和尾数进行了规定:
- 尾数 M 的第一位总是 1(因为 1 <= M < 2),因此这个 1 可以省略不写,它是个隐藏位,这样单精度 23 位尾数可以表示了 24 位有效数字,双精度 52 位尾数可以表示 53 位有效数字
- 指数 E 是个无符号整数,表示 float 时,一共占 8 bit,所以它的取值范围为 0 ~ 255。但因为指数可以是负的,所以规定在存入 E 时在它原本的值加上一个中间数 127,这样 E 的取值范围为 -127 ~ 128。表示 double 时,一共占 11 bit,存入 E 时加上中间数 1023,这样取值范围为 -1023 ~ 1024。
除了规定尾数和指数位,还做了以下规定:
- 指数 E 非全 0 且非全 1:规格化数字,按上面的规则正常计算
- 指数 E 全 0,尾数非 0:非规格化数,尾数隐藏位不再是 1,而是 0(M = 0.xxxxx),这样可以表示 0 和很小的数
- 指数 E 全 1,尾数全 0:正无穷大/负无穷大(正负取决于 S 符号位)
- 指数 E 全 1,尾数非 0:NaN(Not a Number)
浮点数为什么有精度损失?
如果我们现在想用浮点数表示 0.2,0.2 转换为二进制数的过程为,不断乘以 2,直到不存在小数为止,在这个计算过程中,得到的整数部分从上到下排列就是二进制的结果。
所以 0.2(D) = 0.00110…(B)。
因为十进制的 0.2 无法精确转换成二进制小数,而计算机在表示一个数字时,宽度是有限的,无限循环的小数存储在计算机时,只能被截断,所以就会导致小数精度发生损失的情况。