【发布时间】:2021-05-13 10:48:17
【问题描述】:
定点数的理论是我们在整数部分和小数部分之间划分一定数量的位。这个数额是固定的。
为了从浮点数转换为定点数,我们遵循以下算法:
-
计算 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