整数示例适用于32位的int
算术运算,DWORD
是32位的unsigned int
- 浮点数
pow(x,y)=x^y
通常是这样计算的:
因此,分数指数可以计算:pow(x,y) = exp2(y*log2(x))
。这也可以在定点上完成:
- 整数
pow(a,b)=a^b
其中a>=0 , b>=0
这很容易(你已经有了)通过平方完成:
DWORD powuu(DWORD a,DWORD b)
{
int i,bits=32;
DWORD d=1;
for (i=0;i<bits;i++)
{
d*=d;
if (DWORD(b&0x80000000)) d*=a;
b<<=1;
}
return d;
}
- 整数
pow(a,b)=a^b
,其中b>=0
只需添加几个if
来处理负数a
int powiu(int a,DWORD b)
{
int sig=0,c;
if ((a<0)&&(DWORD(b&1)) { sig=1; a=-a; }
c=powuu(a,b); if (sig) c=-c;
return c;
}
- 整数
pow(a,b)=a^b
如果b<0
,那么意味着1/powiu(a,-b)
。你可以看到结果根本不是整数,所以要么忽略这种情况,要么返回浮点值,或者添加一个乘数变量(这样你就可以在纯整数算术上评估PI
方程)。这是浮点数结果:
float powfii(int a,int b)
{
if (b<0) return 1.0/float(powiu(a,-b));
else return powiu(a,b);
}
- 整数
pow(a,b)=a^b
,其中 b
是分数
您可以这样做:a^(1/bb)
,其中 bb
是整数。实际上,这是求根运算,因此可以使用二分查找进行评估:
a^(1/2)
是 平方根(a)
a^(1/bb)
是 bb次方根(a)
因此,从最高位到最低位对c
进行二分查找,并计算是否满足 pow(c,bb)<=a
,如果是,则保留该bit
,否则清除它。这是一个sqrt
的例子:
int bits(DWORD p)
{
DWORD m=0x80000000; int b=32;
for (;m;m>>=1,b--)
if (p>=m) break;
return b;
}
DWORD sqrt(const DWORD &x)
{
DWORD m,a;
m=(bits(x)>>1);
if (m) m=1<<m; else m=1;
for (a=0;m;m>>=1) { a|=m; if (a*a>x) a^=m; }
return a;
}
现在只需将
if (a*a>x)
更改为
if (pow(a,bb)>x)
,其中
bb=1/b
...因此
b
是您要查找的分数指数,而
bb
是整数。另外,
m
是结果的位数,所以将
m=(bits(x)>>1);
更改为
m=(bits(x)/bb);
[编辑1] 固定点平方根示例
//---------------------------------------------------------------------------
const int _fx32_fract=16
const int _fx32_one =1<<_fx32_fract
DWORD fx32_mul(const DWORD &x,const DWORD &y) // unsigned fixed point mul
{
DWORD a=x,b=y
asm { // compute (a*b)>>_fx32_fract
mov eax,a // eax=a
mov ebx,b // ebx=b
mul eax,ebx // (edx,eax)=eax*ebx
mov ebx,_fx32_one
div ebx // eax=(edx,eax)>>_fx32_fract
mov a,eax
}
return a
}
DWORD fx32_sqrt(const DWORD &x) // unsigned fixed point sqrt
{
DWORD m,a
if (!x) return 0
m=bits(x)
if (m>_fx32_fract) m-=_fx32_fract
m>>=1
m=_fx32_one<<m
for (a=0
{
a|=m
if (fx32_mul(a,a)>x) // if result is too big
a^=m
}
return a
}
//---------------------------------------------------------------------------
这是无符号定点数。高16位为整数部分,低16位为小数部分。
- 将fx转换为fp:
DWORD(float(x)*float(_fx32_one))
- 将fp转换为fx:
float(DWORD(x))/float(_fx32_one))
fx32_mul(x,y)
是 x*y
,它使用 80386+ 32位架构的汇编语言(您可以将其重写为 Karatsuba 或其他平台无关的算法)
fx32_sqrt(x)
是 sqrt(x)
在定点数中,应注意乘法的小数位移:(a<<16)*(b<<16)=(a*b<<32)
,需要通过>>16
向右移动以获得结果(a*b<<16)
。此外,结果可能会溢出32位,因此我在汇编中使用64位结果。
[编辑2] 32位有符号定点数pow C++示例
当您将所有先前的步骤结合起来时,应该得到类似于以下内容:
const int _fx32_bits=32;
const int _fx32_fract_bits=16;
const int _fx32_integ_bits=_fx32_bits-_fx32_fract_bits;
const int _fx32_one =1<<_fx32_fract_bits;
const float _fx32_onef =_fx32_one;
const int _fx32_fract_mask=_fx32_one-1;
const int _fx32_integ_mask=0xFFFFFFFF-_fx32_fract_mask;
const int _fx32_sMSB_mask =1<<(_fx32_bits-1);
const int _fx32_uMSB_mask =1<<(_fx32_bits-2);
float fx32_get(int x) { return float(x)/_fx32_onef; }
int fx32_set(float x) { return int(float(x*_fx32_onef)); }
int fx32_mul(const int &x,const int &y)
{
int a=x,b=y;
asm {
mov eax,a
mov ebx,b
mul eax,ebx
mov ebx,_fx32_one
div ebx
mov a,eax;
}
return a;
}
int fx32_div(const int &x,const int &y)
{
int a=x,b=y;
asm {
mov eax,a
mov ebx,_fx32_one
mul eax,ebx
mov ebx,b
div ebx
mov a,eax;
}
return a;
}
int fx32_abs_sqrt(int x)
{
int m,a;
if (!x) return 0;
if (x<0) x=-x;
m=bits(x);
for (a=x,m=0;a;a>>=1,m++);
m-=_fx32_fract_bits;
if (m<0) m=0; m>>=1;
m=_fx32_one<<m;
for (a=0;m;m>>=1)
{
a|=m;
if (fx32_mul(a,a)>x)
a^=m;
}
return a;
}
int fx32_pow(int x,int y)
{
if (!y) return _fx32_one;
if (!x) return 0;
if (y==-_fx32_one) return fx32_div(_fx32_one,x);
if (y==+_fx32_one) return x;
int m,a,b,_y; int sx,sy;
sx=0; if (x<0) { sx=1; x=-x; }
sy=0; if (y<0) { sy=1; y=-y; }
_y=y&_fx32_fract_mask;
y=y&_fx32_integ_mask;
a=_fx32_one;
if (y)
{
for (m=_fx32_uMSB_mask;(m>_fx32_one)&&(m>y);m>>=1);
for (;m>=_fx32_one;m>>=1)
{
a=fx32_mul(a,a);
if (int(y&m)) a=fx32_mul(a,x);
}
}
if (_y)
{
for (b=x,m=_fx32_one>>1;m;m>>=1)
{
b=fx32_abs_sqrt(b);
if (int(_y&m)) a=fx32_mul(a,b);
}
}
if (sy) { if (a) a=fx32_div(_fx32_one,a); else a=0; }
if (sx) { if (_y) a=0; else if(int(y&_fx32_one)) a=-a; }
return a;
}
我这样测试过它:
float a,b,c0,c1,d;
int x,y;
for (a=0.0,x=fx32_set(a);a<=10.0;a+=0.1,x=fx32_set(a))
for (b=-2.5,y=fx32_set(b);b<=2.5;b+=0.1,y=fx32_set(b))
{
if (!x) continue;
if (!y) continue;
c0=pow(a,b);
c1=fx32_get(fx32_pow(x,y));
d=0.0;
if (fabs(c1)<1e-3) d=c1-c0; else d=(c0/c1)-1.0;
if (fabs(d)>0.1)
d=d;
}
a,b
是浮点数
x,y
是 a,b
最接近的定点表示
c0
是 math pow 的结果
c1
是 fx32_pow 的结果
d
是差异
希望没有忘记什么琐碎的东西,但看起来它似乎正常工作。请注意,定点具有非常有限的精度,因此结果会略有不同...
P.S. 看一下这个:
x^n = (x^-1)^(-n)
。 - amitx^n = (x^-1)^(-n)
。 - amitif (x&1)
替换为if (exp % 2)
。 - Chan Kha Vu