这个 SO 问题的答案在实施方面非常全面。这里的解释比我在那里看到的要多一点:
一种方法是将所有数字强制在一个范围内,例如 [-1.0,1.0)。然后将这些数字映射到范围 [-2^15,(2^15)-1] 中。例如,
Half = round(0.5*32768); //16384
Third = round((1.0/3.0)*32768); //10923
当您将这两个数字相乘时,您会得到
Temp = Half*Third; //178962432
Result = Temp/32768; //5461 = round(1.0/6.0)*32768
最后一行中除以 32768 是Patros提出的关于需要额外缩放步骤的乘法的观点。如果您明确编写 2^N 缩放比例,这将更有意义:
x1 = x1Float*(2^15);
x2 = x2Float*(2^15);
Temp = x1Float*x2Float*(2^15)*(2^15);
Result = Temp/(2^15); //get back to 2^N scaling
所以这就是算术。对于实现,请注意两个 16 位整数的乘法需要 32 位结果,因此 Temp 应为 32 位。此外,32768 不能用 16 位变量表示,因此请注意编译器将生成 32 位立即数。正如您已经指出的那样,您可以转换为乘/除以 2 的幂,这样您就可以编写
N = 15;
SInt16 x1 = round(x1Float * (1 << N));
SInt16 x2 = round(x2Float * (1 << N));
SInt32 Temp = x1*x2;
Result = (SInt16)(Temp >> N);
FloatResult = ((double)Result)/(1 << N);
但是假设 [-1,1) 不是正确的范围?如果您希望将数字限制为 [-4.0,4.0),则可以使用 N = 13。然后您有 1 个符号位,二进制点之前有 2 位,之后有 13 位。这些分别称为 1.15 和 3.13 定点小数类型。你用分数的精度换取净空。
只要您注意饱和度,添加和减去小数类型就可以正常工作。正如帕特罗斯所说,对于除法,缩放实际上抵消了。所以你必须做
Quotient = (x1/x2) << N;
或者,为了保持精度
Quotient = (SInt16)(((SInt32)x1 << N)/x2); //x1 << N needs wide storage
乘以和除以整数正常工作。例如,要除以 6,您可以简单地写
Quotient = x1/6; //equivalent to x1Float*(2^15)/6, stays scaled
在除以 2 的幂的情况下,
Quotient = x1 >> 3; //divides by 8, can't do x1 << -3 as Patros pointed out
但是,添加和减去整数并不是天真的工作。您必须首先查看整数是否适合您的 xy 类型,制作等效的小数类型,然后继续。
我希望这对这个想法有所帮助,请查看其他问题中的代码以获得干净的实现。