我们如何将浮点数转换为"定点表示",并使用他们的"定点表示"在定点运算中,例如加法和乘法?当转换回浮点时,定点运算的结果必须得到正确的答案。
说:
(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);
}
如果我想添加(双)(1.00)+(双)(2.00)=应该屈服于(双倍)(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。
HERE' THE CATCH:
所以我知道我的代码正在运行,但是如何在这些定点表示上执行加法,乘法和减法,而不需要内部浮点运算和数字。
因此在上面的代码中,函数 sum_fixed,product_fixed和subtract_fixed 具有内部浮点数(斜率和1.65,1.65是MAX浮点输入)。我通过基本数学得出了我的代码,真的。
所以我想实现加,减和产品函数,而不需要任何内部浮点运算或数字。
更新
我还发现了将分数转换为定点的更简单的代码:
//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定点。我想将SIGNED分数(-1.65到1.65)转换为UNSIGNED定点(0x0000到0x03FF)。如何使用上面的代码执行此操作?位数或位数是否与转换过程有关?此代码仅适用于正分数吗?
归功于@chux
答案 0 :(得分:1)
您可以使您的数字的浮点表示的尾数等于其固定点表示。由于FP加法移动较小的操作数的尾数,直到两个操作数具有相同的指数,您可以添加一个“幻数”来强制它。对于double,它是1&lt;(52-精度)(52是双尾数的尾数,'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 <&lt;&lt; precision);
请注意,它不处理底片。如果你需要它们,你必须手动将它们转换为互补表示(首先制作双精度,然后如果输入为负则否定int结果)。