liangbch
发表于 2009-2-12 12:26:16
应该是,用浮点指令可以做的更好。
刚刚完成了一个完全用整数指令将浮点数转化为整数的程序,见下,这个函数唯一不足之处是专为0将出错。//*p必须是整数或者0
int double2int(double *p)
{
_asm
{
moveax,p
movecx,dword ptr
movedx,0xfff00000
moveax,0xfffff
andedx,ecx //阶码
andeax,ecx //尾数
shredx,20 //得到阶段码
addeax,0x100000 //设置位数最高有效位
movecx,1043
subecx,edx
shreax,cl //得到整数
}
}
void testdouble2int()
{
double f;
int n;
f=23.4567;
n=double2int(&f);
printf("%lf=%d\n",f,n);
f=23.9845;
n=double2int(&f);
printf("%lf=%d\n",f,n);
f=2.4567;
n=double2int(&f);
printf("%lf=%d\n",f,n);
f=2.9845;
n=double2int(&f);
printf("%lf=%d\n",f,n);
f=65534.4567;
n=double2int(&f);
printf("%lf=%d\n",f,n);
f=65534.9977;
n=double2int(&f);
printf("%lf=%d\n",f,n);
f=1.00;
n=double2int(&f);
printf("%lf=%d\n",f,n);
f=0;
n=double2int(&f);
printf("%lf=%d\n",f,n);
}HouSisong 写的类似的函数是这样的, 见http://blog.csdn.net/housisong/archive/2007/05/19/1616026.aspxinline long _ftol_ieee(float f)
{
long a = *(long*)(&f);
unsigned long mantissa= (a&((1<<23)-1))|(1<<23); //不支持非规格化浮点数
long exponent= ((a&0x7fffffff)>>23);
long r = (mantissa<<8) >> (31+127-exponent);
long sign = (a>>31);
return ((r ^ (sign)) - sign ) &~ ((exponent-127)>>31);
}
[ 本帖最后由 liangbch 于 2009-2-12 12:30 编辑 ]
无心人
发表于 2009-2-12 13:14:43
早上想是否32位浮点足够,中午发现,可能存在精度丢失
实际计算下
Prelude> let b = 2.0**32 - 1.0
Prelude> b
4.294967295e9
Prelude> let c = sqrt b
Prelude> c
65535.999992370605
Prelude> let d = 255.0 / 256.0
Prelude> d
0.99609375
无法满足要求
========================
我也想用整数处理浮点截断
呵呵
看来只能考虑64位浮点了
下午调试好另外三个就考虑下
无心人
发表于 2009-2-12 13:16:49
上面的帖子,我提到了
用修改RC的方法需要多9条指令
且存在16位计算
你的代码是11条
很可能比fistp快
可以考虑
===========================
不对
需要额外的两个压栈指令
和存储
即
push 0
push 0
fst qword ptr
无心人
发表于 2009-2-12 13:19:59
64位浮点
0的话,指数和尾数都是0
1是指数乃1023,尾数是0
看是否有避免跳转判断的代码
无心人
发表于 2009-2-12 13:48:25
目前的三个版本最终代码double b32[] = {0.0, 4294967296.0};
__declspec(naked)
DWORD __fastcall iSqrt_SSE3(DWORD n)
{
__asm
{
push ecx
mov eax, ecx
and eax, 0x80000000
shr eax, 31
fld qword ptr
fild dword ptr
faddp st(1), st
fsqrt
fisttp dword ptr
pop eax
ret
}
}
__declspec(naked)
DWORD __fastcall iSqrt_SSE2(DWORD n)
{
__asm
{
cvtsi2sd xmm0, ecx
mov eax, ecx
and eax, 0x80000000
shr eax, 31
movsd xmm1, qword ptr
addsd xmm0, xmm1
sqrtsd xmm0, xmm0
cvttsd2si eax, xmm0
ret
}
}
__declspec(naked)
DWORD __fastcall iSqrt_FPU(DWORD n)
{
__asm
{
push ecx
sub esp, 4
fnstcw word ptr
mov edx, dword ptr
or dword ptr , 0x0C00
mov eax, ecx
and eax, 0x80000000
shr eax, 31
fld qword ptr
fild dword ptr
faddp st(1), st
fsqrt
fldcw word ptr
fistp dword ptr
mov dword ptr , edx
fldcw word ptr
add esp, 4
pop eax
ret
}
}
无心人
发表于 2009-2-12 14:03:46
Test
FPU: sqrt(1023) = 31
FPU: sqrt(4000000000) = 63245
SSE2: sqrt(1023) = 31
SSE2: sqrt(4000000000) = 63245
SSE3: sqrt(1023) = 31
SSE3: sqrt(4000000000) = 63245
FPU Version: 158930.292 ms
SSE2 Version: 177428.864 ms
SSE3 Version: 155667.881 ms
========================
从1到2^32-1循环
无心人
发表于 2009-2-12 14:26:28
可以看到
FPU版的控制 RC方式仅比fisttp的SSE3指令略微多一点
计算指令周期
FPU: 59.2 clock
SSE2:66.1 clock
SSE3: 58.0 clock
liangbch
发表于 2009-2-12 15:00:41
你的35楼的 FPU 版本在我这里无法运行,当执行到 fld qword ptr 时,程序崩溃。
另外,你之前写的FPU版本也有问题, FPU 入栈操作多余出栈,最后当使用fld 时因栈满,到导致st(0)变为一个无效值。
另外,你24楼的程序也有问题,在我这里运行时,当执行到 movsd xmm1, qword ptr ,程序崩溃。
下面给出一个FPU的版本,这个程序做到栈平衡,但当被开放数是1,4,9,16,25,49等完全平方数时,计算结果错误。
double b32 = 4294967296.0;
double zero5= -0.5;
__declspec(naked)
DWORD __fastcall iSqrt_FPU1(DWORD n)
{
__asm
{
push ecx
fld qword ptr
fldz
cmp ecx, 0x8000000
fcmovnb st, st(1)
fild dword ptr
faddpst(1),st
fsqrt
faddqword ptr
fistpdword ptr
fstpst
pop eax
ret
}
}
liangbch
发表于 2009-2-12 15:31:12
在贴一个FPU的版本,完全不是用 SSE/SSE2指令,可正常工作。__declspec(naked)
DWORD __fastcall iSqrt_FPU2(DWORD n)
{
__asm
{
or ecx,ecx
jnz next00
mov eax,0
ret
next00:
push ecx
fld qword ptr
fldz
cmp ecx, 0x80000000
fcmovnb st, st(1)
fild dword ptr
faddpst(1),st
fsqrt
fstpqword ptr
fstpst
fwait
movecx,dword ptr
movedx,0xfff00000
moveax,0xfffff
andedx,ecx //阶码
andeax,ecx //尾数
shredx,20 //得到阶段码
addeax,0x100000 //设置位数最高有效位
movecx,1043
subecx,edx
shreax,cl //得到整数
pop ecx
ret
}
}
无心人
发表于 2009-2-12 15:51:27
老大啊
我已经修正了栈平衡问题
35#的代码是正确的
另外,这两个代码都需要定义个
double b32 [] = {0.0, 4294967296.0};
我早已经偷偷的把
double b32 = 4294967296.0;
改了啊
否则要用到FCMOVcc等复杂指令的
页:
1
2
3
[4]
5
6
7
8
9
10
11
12
13