C++ 如何将定点数理论与它的实际实现联系起来?

C++ 如何将定点数理论与它的实际实现联系起来?,c++,fixed-point,C++,Fixed Point,不动点数的理论是我们在整数部分和小数部分之间划分一定数量的位。这个数额是固定的 例如,26.5按以下顺序存储: 要将浮点转换为定点,我们遵循以下算法: 计算x=浮点输入*2^(小数位) 将x四舍五入到最接近的整数(例如,四舍五入(x)) 将舍入的x存储在整数容器中 现在,如果我们观察数字的位表示,以及2^(分数位)上的乘法,我们将看到: 27 is 11011 27*2^10 is 110 1100 0000 0000 which is shifting on 10 bit

不动点数的理论是我们在整数部分和小数部分之间划分一定数量的位。这个数额是固定的

例如,26.5按以下顺序存储:


要将浮点转换为定点,我们遵循以下算法:

  • 计算x=浮点输入*2^(小数位)

  • 将x四舍五入到最接近的整数(例如,四舍五入(x))

  • 将舍入的x存储在整数容器中


现在,如果我们观察数字的位表示,以及2^(分数位)上的乘法,我们将看到:

27 is 11011
27*2^10 is 110 1100 0000 0000    which is shifting on 10 bits to the left.
所以我们可以说,在2^10上相乘确实给了我们在比特右边的“空间”来保存这个数字。我们可以用这种方式将两个这样的数字进行转换,相互作用,最终通过在2^10上进行相反的除法,重新转换为熟悉的点视图

如果我们回想一下,位存储在某个整数变量中,而该变量又有自己的位数,那么很明显,该变量中用于分数部分的位数越多,用于整数部分的位数就越少

27.3 * 2^10 = 27955.2 should be rounded for storing in integer type to
27955 which is 110 1101 0011 0011
在那个数字可以被改变之后,某些值现在就不重要了,比如说,我们想找回人类可读的值:

27955/2^10 = 27,2998046875

点后的位数是多少? 假设我们有两个数字用于相乘,我们在点后选择了10位

27 * 3.3 = 89.1 expected
27*2^10 = 27 648 is 110 1100 0000 0000
3.3*2^10 = 3 379 is     1101 0011 0011
27 648 * 3 379 = 93 422 592
consequently 
27*3.3 = 93 422 592/(2^10*2^10) = 89.09 pretty accurate
27 and 3.3
27*2^1 = 54 is 110110
3.3*2^1 = 6.6 after round 6 is 110
54 * 6 = 324
consequently 
27*3.3 = 324/(2^1*2^1) = 81 which is unsatisfying
让我们在点后取1位

27 * 3.3 = 89.1 expected
27*2^10 = 27 648 is 110 1100 0000 0000
3.3*2^10 = 3 379 is     1101 0011 0011
27 648 * 3 379 = 93 422 592
consequently 
27*3.3 = 93 422 592/(2^10*2^10) = 89.09 pretty accurate
27 and 3.3
27*2^1 = 54 is 110110
3.3*2^1 = 6.6 after round 6 is 110
54 * 6 = 324
consequently 
27*3.3 = 324/(2^1*2^1) = 81 which is unsatisfying

在实践中,我们可以使用下一个代码创建和操作定点编号:

#include <iostream>

using namespace std;

const int scale = 10;

#define DoubleToFixed(x) (x*(double)(1<<scale))
#define FixedToDouble(x) ((double)x / (double)(1<<scale))
#define IntToFixed(x) (x << scale)
#define FixedToInt(x) (x >> scale)
#define MUL(x,y) (((x)*(y)) >> scale)
#define DIV(x,y) ((x) << scale)

int main()
{
double a = 7.27;
double b = 3.0;

int f = DoubleToFixed(a);
cout << f<<endl;                  //7444
cout << FixedToDouble(f)<<endl;   //7.26953125

int g = DoubleToFixed(b);
cout << g<<endl;                  //3072
int c = MUL(f,g);
cout << FixedToDouble(c)<<endl;   //21.80859375

}
#包括
使用名称空间std;
const int scale=10;

#定义DoubleToFixed(x)(x*(double)(1定点格式被用作表示小数的一种方式。处理器通常比浮点运算执行定点或整数运算更快或更有效。定点运算是否适用于应用程序取决于应用程序需要处理的数字

使用定点格式确实需要将输入转换为定点格式,并将定点格式中的数字转换为输出。但整数和浮点也是如此:所有输入都必须转换为用于表示它的任何内部格式,所有输出都必须通过从内部格式转换生成

2^(分数位)上的乘法如何影响点后的位数

假设我们有一些数字x,表示为整数x=x•2f,其中f是分数位数。从概念上讲,x是定点格式。类似地,我们有y表示为y=y•2f

如果我们执行一个整数乘法指令来产生结果Z=XY,那么Z=XY=(x•2f)•(y•2f)=XY•22f。然后,如果我们将Z除以2f(或者,几乎等价地,将其右移f位),我们有xy•2f,除法中可能出现的任何舍入误差除外。xy•2f是x和y乘积的定点表示

因此,我们可以通过执行整数乘法和移位来实现定点乘法

通常,为了得到舍入而不是截断,在移位之前加上2f的一半,因此我们计算地板((XY+2f)−1) /2f):

  • 用X乘以Y
  • 添加2f−一,
  • 右移f位

定点格式被用作表示小数的一种方式。处理器通常比浮点运算执行定点或整数运算更快或更有效。定点运算是否适合应用程序取决于应用程序需要处理的数字

使用定点格式确实需要将输入转换为定点格式,并将定点格式中的数字转换为输出。但整数和浮点也是如此:所有输入都必须转换为用于表示它的任何内部格式,所有输出都必须通过从内部格式转换生成

2^(分数位)上的乘法如何影响点后的位数

假设我们有一些数字x,表示为整数x=x•2f,其中f是分数位数。从概念上讲,x是定点格式。类似地,我们有y表示为y=y•2f

如果我们执行一个整数乘法指令来产生结果Z=XY,那么Z=XY=(x•2f)•(y•2f)=XY•22f。然后,如果我们将Z除以2f(或者,几乎等价地,将其右移f位),我们有xy•2f,除法中可能出现的任何舍入误差除外。xy•2f是x和y乘积的定点表示

因此,我们可以通过执行整数乘法和移位来实现定点乘法

通常,为了得到舍入而不是截断,在移位之前加上2f的一半,因此我们计算地板((XY+2f)−1) /2f):

  • 用X乘以Y
  • 添加2f−一,
  • 右移f位
似乎定点数字只是encreese性能的转换

您还可以说,浮点数是一种增加可表示范围的转换

无论数字最初采用何种格式(字符串、电压电平、整数等),通常都会将它们转换为浮点数以存储或操作它们,但无论是浮点数还是固定点数都不是人类可读的表示形式

浮点数具有较低的精度和较宽的幅度范围;固定点数具有较高的精度和较窄的幅度范围。(性能差异取决于体系结构和重要操作。)不应将定点表示视为转换