找回密码
 欢迎注册
楼主: 无心人

[讨论] 二进制32位整数快速平方根

[复制链接]
发表于 2009-2-12 12:26:16 | 显示全部楼层
应该是,用浮点指令可以做的更好。
刚刚完成了一个完全用整数指令将浮点数转化为整数的程序,见下,这个函数唯一不足之处是专为0将出错。
  1. //*p必须是整数或者0
  2. int double2int(double *p)
  3. {
  4.         _asm
  5.         {
  6.                 mov  eax,p
  7.                 mov  ecx,dword ptr [eax+4]
  8.                 mov  edx,0xfff00000
  9.                 mov  eax,0xfffff
  10.                
  11.                 and  edx,ecx        //阶码
  12.                 and  eax,ecx        //尾数

  13.                 shr  edx,20                //得到阶段码
  14.                 add  eax,0x100000 //设置位数最高有效位
  15.                 mov  ecx,1043
  16.                 sub  ecx,edx
  17.                 shr  eax,cl                //得到整数
  18.                

  19.         }
  20. }

  21. void testdouble2int()
  22. {
  23.         double f;
  24.         int n;

  25.         f=23.4567;
  26.         n=double2int(&f);
  27.         printf("%lf=%d\n",f,n);

  28.         f=23.9845;
  29.         n=double2int(&f);
  30.         printf("%lf=%d\n",f,n);


  31.         f=2.4567;
  32.         n=double2int(&f);
  33.         printf("%lf=%d\n",f,n);
  34.        
  35.         f=2.9845;
  36.         n=double2int(&f);
  37.         printf("%lf=%d\n",f,n);


  38.         f=65534.4567;
  39.         n=double2int(&f);
  40.         printf("%lf=%d\n",f,n);

  41.         f=65534.9977;
  42.         n=double2int(&f);
  43.         printf("%lf=%d\n",f,n);
  44.        

  45.         f=1.00;
  46.         n=double2int(&f);
  47.         printf("%lf=%d\n",f,n);

  48.         f=0;
  49.         n=double2int(&f);
  50.         printf("%lf=%d\n",f,n);
  51. }
复制代码
HouSisong 写的类似的函数是这样的, 见http://blog.csdn.net/housisong/archive/2007/05/19/1616026.aspx
  1. inline long _ftol_ieee(float f)
  2. {
  3.        long a         = *(long*)(&f);
  4.        unsigned long mantissa  = (a&((1<<23)-1))|(1<<23); //不支持非规格化浮点数
  5.        long exponent  = ((a&0x7fffffff)>>23);
  6.        long r         = (mantissa<<8) >> (31+127-exponent);
  7.        long sign      = (a>>31);
  8.        return ((r ^ (sign)) - sign ) &~ ((exponent-127)>>31);
  9. }
复制代码

[ 本帖最后由 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 [esp]
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2009-2-12 13:19:59 | 显示全部楼层
64位浮点
0的话,指数和尾数都是0
1是指数乃1023,尾数是0

看是否有避免跳转判断的代码
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2009-2-12 13:48:25 | 显示全部楼层
目前的三个版本最终代码
  1. double b32[] = {0.0, 4294967296.0};

  2. __declspec(naked)
  3. DWORD __fastcall iSqrt_SSE3(DWORD n)
  4. {
  5.         __asm
  6.         {
  7.         push ecx
  8.         mov eax, ecx   
  9.     and eax, 0x80000000
  10.     shr eax, 31
  11.         fld qword ptr [b32 + eax * 8]
  12.         fild dword ptr [esp]
  13.     faddp st(1), st
  14.         fsqrt
  15.         fisttp dword ptr [esp]
  16.         pop eax
  17.         ret
  18.         }
  19. }

  20. __declspec(naked)
  21. DWORD __fastcall iSqrt_SSE2(DWORD n)
  22. {
  23.         __asm
  24.         {
  25.     cvtsi2sd xmm0, ecx
  26.         mov eax, ecx
  27.         and eax, 0x80000000
  28.         shr eax, 31
  29.         movsd xmm1, qword ptr [b32 + eax * 8]
  30.         addsd xmm0, xmm1
  31.         sqrtsd xmm0, xmm0
  32.         cvttsd2si eax, xmm0
  33.         ret
  34.         }
  35. }

  36. __declspec(naked)
  37. DWORD __fastcall iSqrt_FPU(DWORD n)
  38. {
  39. __asm
  40. {
  41.    push ecx
  42.    sub esp, 4
  43.    fnstcw word ptr  [esp]
  44.    mov edx, dword ptr [esp]
  45.    or dword ptr [esp], 0x0C00
  46.    mov eax, ecx   
  47.    and eax, 0x80000000
  48.    shr eax, 31
  49.    fld qword ptr [b32 + eax * 8]
  50.    fild dword ptr [esp + 4]
  51.    faddp st(1), st
  52.    fsqrt
  53.    fldcw word ptr [esp]
  54.    fistp dword ptr [esp + 4]
  55.    mov dword ptr [esp], edx
  56.    fldcw word ptr [esp]
  57.    add esp, 4
  58.    pop eax
  59.    ret
  60.    }
  61. }
复制代码
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 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
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2009-2-12 15:00:41 | 显示全部楼层
你的35楼的 FPU 版本在我这里无法运行,当执行到 fld qword ptr [b32 + eax * 8] 时,程序崩溃。
另外,你之前写的FPU版本也有问题, FPU 入栈操作多余出栈,最后当使用fld 时因栈满,到导致st(0)变为一个无效值。
另外,你24楼的程序也有问题,在我这里运行时,当执行到 movsd xmm1, qword ptr [b32 + eax * 4]  ,程序崩溃。

下面给出一个FPU的版本,这个程序做到栈平衡,但当被开放数是1,4,9,16,25,49等完全平方数时,计算结果错误。

  1. double b32 = 4294967296.0;
  2. double zero5= -0.5;
  3. __declspec(naked)
  4. DWORD __fastcall iSqrt_FPU1(DWORD n)
  5. {
  6. __asm
  7. {
  8. push ecx
  9. fld qword ptr [b32]
  10. fldz   
  11. cmp ecx, 0x8000000
  12. fcmovnb st, st(1)
  13. fild dword ptr [esp]
  14. faddp  st(1),st
  15. fsqrt
  16. fadd  qword ptr [zero5]
  17. fistp  dword ptr [esp]
  18. fstp  st
  19. pop eax
  20. ret
  21. }
  22. }
复制代码
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2009-2-12 15:31:12 | 显示全部楼层
在贴一个FPU的版本,完全不是用 SSE/SSE2指令,可正常工作。
  1. __declspec(naked)
  2. DWORD __fastcall iSqrt_FPU2(DWORD n)
  3. {
  4. __asm
  5. {
  6. or ecx,ecx
  7. jnz next00
  8. mov eax,0
  9. ret
  10. next00:
  11. push ecx
  12. fld qword ptr [b32]
  13. fldz   
  14. cmp ecx, 0x80000000
  15. fcmovnb st, st(1)
  16. fild dword ptr [esp]
  17. faddp  st(1),st
  18. fsqrt
  19. fstp  qword ptr [esp-8]
  20. fstp  st

  21. fwait
  22. mov  ecx,dword ptr [esp-4]
  23. mov  edx,0xfff00000
  24. mov  eax,0xfffff

  25. and  edx,ecx //阶码
  26. and  eax,ecx //尾数
  27. shr  edx,20 //得到阶段码
  28. add  eax,0x100000 //设置位数最高有效位
  29. mov  ecx,1043
  30. sub  ecx,edx
  31. shr  eax,cl //得到整数

  32. pop ecx
  33. ret
  34. }
  35. }
复制代码
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2009-2-12 15:51:27 | 显示全部楼层
老大啊

我已经修正了栈平衡问题
35#的代码是正确的
另外,这两个代码都需要定义个
double b32 [] = {0.0, 4294967296.0};
我早已经偷偷的把
double b32 = 4294967296.0;
改了啊

否则要用到FCMOVcc等复杂指令的
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
您需要登录后才可以回帖 登录 | 欢迎注册

本版积分规则

小黑屋|手机版|数学研发网 ( 苏ICP备07505100号 )

GMT+8, 2024-4-19 23:18 , Processed in 0.044494 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表