我想首先说这不是关于优化的,所以请不要将这个主题拖到那条路上。我使用定点算术的目的是因为我想在不使用浮点的情况下控制计算的精度。
有了这个说,让我们继续前进。我想要17位作为范围,15位作为小数部分。额外位用于签名值。以下是一些宏。
const int scl = 18;
#define Double2Fix(x) ((x) * (double)(1 << scl))
#define Float2Fix(x) ((x) * (float)(1 << scl))
#define Fix2Double(x) ((double)(x) / (1 << scl))
#define Fix2Float(x) ((float)(x) / (1 << scl))
加法和减法相当简单,但是对于mul和div来说,事情变得有点棘手。
我已经看到两种不同的方法来处理这两种类型的操作。 1)如果我使用32位,那么使用临时64位变量来存储中间乘法步骤,然后在结束时进行缩放。
2)在乘法步骤中,在乘法之前将两个变量缩放到较小的位范围。例如,如果你有一个32位的寄存器,整个数字是16位,你可以像这样移动:
(((a)>>8)*((b)>>6) >> 2) or some combination that makes sense for you app.
在我看来,如果你设计你的固定点数学大约32位,总是依赖于64位变量能够存储你的中间值,但另一方面转移到较低的比例可能会严重减少你的范围和精度。
问题 既然我想避免尝试强制cpu尝试在我的计算过程中创建一个64位类型,那么转移到较低位值是唯一的另一种选择吗?
我也注意到了
int b = Double2Fix(9.1234567890);
printf("double shift:%f\n",Fix2Double(b));
int c = Float2Fix(9.1234567890);
printf("float shift:%f\n",Fix2Float(c));
double shift:9.123444
float shift:9.123444
这种精确度损失只是使用定点数的一部分吗?
答案 0 :(得分:2)
由于我想避免尝试强制cpu尝试在我的计算过程中创建一个64位类型,因此转移到较低位值是唯一的另一种选择吗?
您必须使用硬件功能,并且您可以找到的唯一可用操作是:
如果指令集具有#3,并且CPU有效地实现它,那么就不必担心它产生的超宽结果。对于x86,您可以将这些视为给定。无论如何,你说这不是一个优化问题:)。
坚持#1,你需要将操作数分解为(N / 2)个比特并进行长时间的乘法,这可能会产生更多的工作。在某些情况下,它仍然是正确的事情,例如在没有它或没有#2的CPU上实现#3(软件扩展算术)。
这种精确度损失只是使用定点数的一部分吗?
log2(9.1234567890 - 9.123444)= -16.25,你使用了16位精度,所以是的,非常典型。