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
查看完整版本: 二进制32位整数快速平方根