如何将 float 转换为其“定点表示”,并在加法和乘法等定点运算中使用其“定点表示”?当定点运算的结果转换回浮点时,必须产生正确的答案。
说:
(double)(xb_double) + (double)(xb_double) = ?
然后我们将两个加数转换为定点表示(整数),
(int)(xa_fixed) + (int)(xb_fixed) = (int) (xsum_fixed)
为了得到 (double)(xsum_double),我们将 (int)(sum_fixed) 转换回浮点并产生相同的答案,
FixedToDouble(xsum_fixed) => xsum_double
具体来说,如果xa_double和xb_double的值范围在-1.65和1.65之间,我想将xa_double和xb_double转换为各自的10位定点表示形式(0x0000到0x03FF)
我已经尝试过
int fixed_MAX = 1023;
int fixed_MIN = 0;
double Value_MAX = 1.65;
double Value_MIN = -1.65;
double slope = ((fixed_MAX) - (fixed_MIN))/((Value_MAX) - (Value_MIN));
int DoubleToFixed(double x)
{
return round(((x) - Value_MIN)*slope + fixed_MIN); //via interpolation method
}
double FixedToDouble(int x)
{
return (double)((((x) + fixed_MIN)/slope) + Value_MIN);
}
int sum_fixed(int x, int y)
{
return (x + y - (1.65*slope)); //analysis, just basic math
}
int subtract_fixed(int x, int y)
{
return (x - y + (1.65*slope));
}
int product_fixed(int x, int y)
{
return (((x * y) - (slope*slope*((1.65*FixedToDouble(x)) + (1.65*FixedToDouble(y)) + (1.65*1.65))) + (slope*slope*1.65)) / slope);
}
如果我想添加 (double)(1.00) + (double)(2.00) = ,它应该产生 (double)(3.00),
用我的代码,
xsum_fixed = DoubleToFixed(1.00) + DoubleToFixed(2.00);
xsum_double = FixedToDouble(xsum_fixed);
我得到了答案:
xsum_double = 3.001613
这非常接近正确答案(双)(3.00)
此外,如果我执行乘法和减法,我将分别得到 2.004839 和 -1.001613。
这里是陷阱:
所以我知道我的代码正在运行,但是如何在没有内部浮点运算和数字的情况下对这些定点表示执行加法、乘法和减法。
因此,在上面的代码中,函数 sum_fixed、product_fixed 和 minus_fixed 具有内部 float (斜率和 1.65,1.65 是最大浮点输入)。我确实是通过基础数学推导出代码的。
所以我想实现加法、减法和乘积函数,而不需要任何内部浮点运算或数字。
更新:
我还发现了将小数转换为定点的更简单的代码:
//const int scale = 16; //1/2^16 in 32 bits
#define DoubleToFixed(x) (int)((x) * (double)(1<<scale))
#define FixedToDouble(x) ((double)(x) / (double)(1<<scale))
#define FractionPart(x) ((x) & FractionMask)
#define MUL(x,y) (((long long)(x)*(long long)(y)) >> scale)
#define DIV(x, y) (((long long)(x)<<16)/(y))
但是,这仅将 UNSIGNED 分数转换为 UNSIGNED 定点。我想将有符号分数(-1.65 到 1.65)转换为无符号定点(0x0000 到 0x03FF)。我该如何使用上面的代码来做到这一点?范围或位数是否与转换过程有关?此代码仅适用于正分数吗?
感谢@chux
最佳答案
您可以让数字的浮点表示的尾数等于其定点表示。由于 FP 加法会移动较小操作数的尾数,直到两个操作数具有相同的指数,因此您可以添加某个“魔数(Magic Number)”来强制它。对于 double 来说,它是 1<<(52- precision)(52 是 double 的尾数大小,“precision”是所需的二进制精度位数)。所以转换看起来像这样:
union { double f; long long i; } u = { xfloat+(1ll<<52-precision) }; // shift x's mantissa
long long xfixed = u.i & (1ll<<52)-1; // extract the mantissa
之后,您可以在整数数学中使用 xfixed (对于乘法,您必须将结果右移“精度”)。要将其转换回 double ,只需将其乘以 1.0/(1 << precision);
请注意,它不处理负数。如果您需要它们,则必须手动将它们转换为互补表示(首先制作 double,然后如果输入为负数则对 int 结果取反)。
关于将 SIGNED 分数转换为 UNSIGNED 定点以进行加法和乘法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34125700/