【问题标题】:How to connect the theory of fixed-point numbers and its practical implementation?如何将定点数的理论与其实际实现联系起来?
【发布时间】:2021-05-13 10:48:17
【问题描述】:

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

例如,26.5 按此顺序存储:


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

  • 计算 x = floating_input * 2^(fractional_bits)

    27.3 * 2^10 = 27955.2
    
  • 将 x 舍入到最接近的整数(例如 round(x))

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


现在,如果我们查看数字的位表示以及乘以 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 确实为我们在位的右侧部分提供了“空间”,以保存更改这个数字。我们可以通过这种方式将两个这样的数字进行转换,相互影响,最终通过 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 中,很明显,其中没有存储点的地方。

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

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

【问题讨论】:

  • 定点算法已在现实世界的计算中使用了半个世纪。不妨说说二进制整数算术的“理论”。

标签: c++ fixed-point


【解决方案1】:

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

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

2^(fractional_bits) 的乘法如何影响点后的位数?

假设我们有一些数字 x 表示为整数 X = x•2f em>,其中 f 是小数位数。从概念上讲,X 是定点格式。同样,我们将 y 表示为 Y = y•2f

如果我们执行一个整数乘法指​​令产生结果Z = XY,那么Z = XY = (x•2f)•(y•2f ) = xy•22f。然后,如果我们将 Z 除以 2f(或者,几乎等效地,将其右移 f 位) ,我们有 xy•2f,除了除法中可能出现的任何舍入错误。而xy•2fxy。

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

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

  • X 乘以 Y
  • 添加 2f-1
  • 右移 f 位。

【讨论】:

    【解决方案2】:

    似乎定点数只是为了提高性能而进行的转换。

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

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

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

    【讨论】:

      【解决方案3】:

      我认为您想要一个包含int 以及固定小数点信息的类。实际上,使用是隐含的,但是您随后定义自己的乘法(例如),它在整个固定点 meaning 上起作用,而不是仅仅乘以底层整数。

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

      【讨论】:

        猜你喜欢
        • 2015-08-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-03-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-25
        相关资源
        最近更新 更多