Fixed Point Arithmetic

定点数

Posted by Hao on February 26, 2021

定点数

定义

定点数是一种实数数据类型,要求小数点后位数固定,有时也要求小数点前位数固定。定点数与更复杂的浮点数相对。 如果程序需要在没有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越小,数值范围越大,但精度就越低。对定点数而言,数值范围与精度是一对矛盾,一个变量要想能够表示比较大的数值范围,必须以牺牲精度为代价;而想精度提高,则数的表示范围就相应地减小。

浮点数相关

浮点数是指数通过科学计数法的形式表示,从而使得数不局限于某种类型的固定小数点位置,从而有更灵活的表达范围,即可以表示的值更大,或者表示的精度更高。

image

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

为了使其表示的数字范围、精度最大化,浮点数标准还对指数和尾数进行了规定:

  1. 尾数 M 的第一位总是 1(因为 1 <= M < 2),因此这个 1 可以省略不写,它是个隐藏位,这样单精度 23 位尾数可以表示了 24 位有效数字,双精度 52 位尾数可以表示 53 位有效数字
  2. 指数 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 无法精确转换成二进制小数,而计算机在表示一个数字时,宽度是有限的,无限循环的小数存储在计算机时,只能被截断,所以就会导致小数精度发生损失的情况。

单精度浮点数 - float

双精度浮点数 - double

Refernece