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

How to connect the theory of fixed-point numbers and its practical implementation?

定点数的理论就是把整数部分和小数部分分开一定的位数。这个金额是固定的。

例如26.5的存储顺序为:


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


现在,如果我们查看数字的位表示以及 2^(fractional_bits) 的乘积,我们将看到:

27 is 11011
27*2^10 is 110 1100 0000 0000    which is shifting on 10 bits to the left.

所以我们可以说,乘以 2^10 确实在位的右侧部分给了我们“space”,以节省对这个数字的更改。我们可以让两个这样的数字以这种方式转换,相互影响,最终通过对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

后取1位
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

}

那么,位间点(2的幂)固定放置的理论与实际实现的联系在哪里呢?如果我们在int中存储固定数字,很明显,它没有地方存储点。

看来定点数只是为了提高性能的转换。并且要在计算后检索人类可读的数字,必须存在相反的转换。

我希望,我理解算法。但是在数字之间放置点的想法只是一个抽象的想法吗?

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

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

And how does multiplying on 2^(fractional_bits) affect the quantity of digits after the point?

假设我们有一些数字 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乘积的定点表示xy.

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

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

  • X 乘以 Y
  • 加2f−1.
  • 右移 f 位。

It seems that fixed-point numbers are just convertion for encreese performance.

你还不如说浮点数是一种增加可表示范围的转换

无论您的数字最初采用何种格式(字符串、电压电平、整数等),您经常将它们转换为浮点数以便对其进行存储或操作,但既不是浮点数也不是定点数是一种人类可读的表示形式。

浮点数精度较低,量级范围较广;定点数具有更高的精度和更窄的幅度范围。 (性能差异取决于体系结构和重要操作。)您不应将定点表示视为从浮点数转换而来,而应将其视为浮点数的替代方法。

我想你想要一个 class 来包装 int 以及固定的小数点信息。实际上,使用是隐式的,但是您随后定义了自己的乘法(例如),它在定点 上工作,意思是 作为一个整体,而不是仅仅乘以底层整数。

你不想留下隐含的含义......以一种强有力的方式让编译器知道它。您不必显式调用您的处理函数;使其成为 class 语义的一部分。