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

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

[复制链接]
 楼主| 发表于 2009-2-12 15:55:41 | 显示全部楼层
你39#代码存在问题
首行应该是or ecx, ecx吧
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2009-2-12 16:03:46 | 显示全部楼层

回复 41# 无心人 的帖子

是,不小心写错了,已修改

下面贴出在无心人的基础上,经我改写的3个FPU的版本。


  1. double b32[] = {0.0,4294967296.0};
  2. //实验证明zero5不能大于0.49999999999636
  3. double zero5=        0.49999999999636;;

  4. __declspec(naked)
  5. DWORD __fastcall iSqrt_FPU1(DWORD n)
  6. {
  7. __asm
  8. {
  9. push ecx

  10. shr ecx, 31
  11. fld qword ptr [b32 + ecx  * 8]
  12. fild dword ptr [esp]
  13. faddp  st(1),st
  14. fsqrt
  15. fsub  qword ptr [zero5]
  16. fistp  dword ptr [esp]
  17. pop eax
  18. ret
  19. }
  20. }

  21. __declspec(naked)
  22. DWORD __fastcall iSqrt_FPU2(DWORD n)
  23. {
  24. __asm
  25. {
  26. or ecx,ecx
  27. jnz next00
  28. mov eax,0
  29. ret
  30. next00:
  31. push ecx

  32. shr ecx, 31
  33. fld qword ptr [b32 + ecx * 8]
  34. fild dword ptr [esp]
  35. faddp  st(1),st
  36. fsqrt
  37. fstp  qword ptr [esp-8]

  38. fwait
  39. mov  ecx,dword ptr [esp-4]
  40. mov  edx,0xfff00000
  41. mov  eax,0xfffff

  42. and  edx,ecx //阶码
  43. and  eax,ecx //尾数
  44. shr  edx,20 //得到阶段码
  45. add  eax,0x100000 //设置位数最高有效位
  46. mov  ecx,1043
  47. sub  ecx,edx
  48. shr  eax,cl //得到整数

  49. pop ecx
  50. ret
  51. }
  52. }

  53. __declspec(naked)
  54. DWORD __fastcall iSqrt_FPU3(DWORD n)
  55. {
  56. __asm
  57. {
  58. push ecx
  59. shr ecx, 31
  60. fld qword ptr [b32 + ecx * 8]
  61. fild dword ptr [esp]
  62. faddp  st(1),st
  63. fsqrt

  64. //设置FPU的取整方式  为了直接使用fistp浮点指令
  65. FNSTCW  [esp-4]            // 保存协处理器控制字,用来恢复
  66. FNSTCW  [esp-6]            // 保存协处理器控制字,用来修改
  67. FWAIT
  68. OR  word ptr [esp-6], 0x0F00    // 改为 RC=11  使FPU向零取整
  69. FLDCW   [esp-6]            // 载入协处理器控制字,RC场已经修改

  70. fistp   dword ptr [esp]

  71. //恢复FPU的取整方式
  72. FWAIT
  73. FLDCW   [esp-4]

  74. pop eax
  75. ret
  76. }
  77. }

复制代码

[ 本帖最后由 liangbch 于 2009-2-13 10:28 编辑 ]
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2009-2-12 16:26:35 | 显示全部楼层
我也写了个FPU的版本,有兴趣的朋友可把相关函数添加进来一并测试一下:
  1. #include < stdio.h >
  2. #include < stdlib.h >
  3. #include < time.h >
  4. #include < float.h >

  5. typedef unsigned int UINT32;
  6. typedef UINT32 __fastcall func_t( UINT32 );

  7. _declspec(naked)
  8. UINT32 __fastcall Sqrt_43F( UINT32 n )
  9. {
  10.     __asm
  11.     {
  12.         xor     eax, eax;
  13.         push    eax;
  14.         push    ecx;

  15.         fild    qword ptr[esp];
  16.         fsqrt;
  17.         pop     eax;

  18.         fistp   dword ptr[esp];
  19.         pop     eax;

  20.         ret;
  21.     }
  22. }

  23. #define FN(ID)      Sqrt_##ID##F /* ex.: Sqrt_43F */

  24. void test_fun( void )
  25. {
  26.     UINT32 i, j;
  27.     UINT32 fID[] = { 43, /* ... */ };
  28.     func_t * funcTable[] = { FN(43), /* FN(..),... */ };
  29.     func_t * pfn;

  30. #ifdef _DEBUG
  31.     UINT32 m = 256;
  32. #else
  33.     UINT32 m = 1UL<<28;
  34.     time_t t;
  35. #endif

  36.     printf( "\n\nn: 0-0x%x\n", m );
  37.     for ( i=0; i<sizeof(fID)/sizeof(fID[0]); ++i )
  38.     {
  39.         pfn = funcTable[i];
  40. #ifdef _DEBUG
  41.         // check result
  42.         for ( j=0; j<=m; ++j )
  43.         {
  44.             printf( "Sqrt_%uF(%u)=%u\n", fID[i], j, pfn(j) );
  45.         }
  46.         system( "pause" );
  47. #else
  48.         t = time(NULL);
  49.         for ( j=m; 0!=j; --j )
  50.         {
  51.             pfn(j);
  52.         }
  53.         t = time(NULL)-t;

  54.         printf( "%u#: %u s\n", fID[i], t );
  55. #endif
  56.     }
  57. }

  58. int main(int argc, char* argv[])
  59. {
  60.     _control87( RC_CHOP | PC_64, MCW_RC | MCW_PC );
  61.     test_fun();

  62.     return 0;
  63. }
复制代码
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2009-2-12 16:42:16 | 显示全部楼层
宝宝:
  减0.5不是个好办法,或许因为别的函数改变了RC, 就反而得不到正确的数值了

锅老大:
你这算偷懒的
在函数外
控制RC获得的性能提升有限的
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2009-2-12 16:44:55 | 显示全部楼层
gxq 的这个版本最简单,但是在程序开头改变了FPU控制字,不知到在运行期间,是不是会影响的一些浮点数学函数。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2009-2-12 17:07:17 | 显示全部楼层

  1. double b32[] = {0.0,4294967296.0};

  2. __declspec(naked)
  3. DWORD __fastcall iSqrt_FPU(DWORD n)
  4. {
  5. __asm
  6. {
  7.    push ecx
  8.    sub esp, 4
  9.    fnstcw word ptr  [esp]
  10.    mov edx, dword ptr [esp]
  11.    or dword ptr [esp], 0x0C00
  12.    mov eax, ecx   
  13.    and eax, 0x80000000
  14.    shr eax, 31
  15.    fld qword ptr [b32 + eax * 8]
  16.    fild dword ptr [esp + 4]
  17.    faddp st(1), st
  18.    fsqrt
  19.    fldcw word ptr [esp]
  20.    fistp dword ptr [esp + 4]
  21.    mov dword ptr [esp], edx
  22.    fldcw word ptr [esp]
  23.    add esp, 4
  24.    pop eax
  25.    ret
  26.    }
  27. }

  28. __declspec(naked)
  29. DWORD iSqrt_FPU1(DWORD n)
  30. {
  31. __asm
  32. {
  33.    sub esp, 4
  34.    fnstcw word ptr  [esp]
  35.    mov edx, dword ptr [esp]
  36.    or dword ptr [esp], 0x0C00
  37.    mov eax, [esp + 8]   
  38.    and eax, 0x80000000
  39.    shr eax, 31
  40.    fld qword ptr [b32 + eax * 8]
  41.    fild dword ptr [esp + 8]
  42.    faddp st(1), st
  43.    fsqrt
  44.    fldcw word ptr [esp]
  45.    fistp dword ptr [esp + 8]
  46.    mov dword ptr [esp], edx
  47.    fldcw word ptr [esp]
  48.    add esp, 4
  49.    mov eax, [esp + 4]
  50.    ret
  51.    }
  52. }

  53. __declspec(naked)
  54. DWORD __fastcall iSqrt_FPU2(DWORD n)
  55. {
  56. __asm
  57. {
  58.    push ecx
  59.    mov eax, ecx   
  60.    and eax, 0x80000000
  61.    shr eax, 31
  62.    fld qword ptr [b32 + eax * 8]
  63.    fild dword ptr [esp]
  64.    faddp st(1), st
  65.    fsqrt
  66.    sub esp, 8
  67.    fstp qword ptr [esp]
  68.    mov edx, dword ptr [esp + 4]
  69.    mov eax, edx
  70.    and edx,0x7ff00000
  71.    and eax,0xfffff
  72.    shr edx, 20
  73.    or eax, 0x100000
  74.    xchg ecx, edx
  75.    sub ecx, 1043
  76.    neg ecx
  77.    shr eax, cl
  78.    xchg edx, ecx
  79.    add esp, 12
  80.    or ecx, ecx
  81.    cmove eax, ecx
  82.    ret
  83.    }
  84. }

复制代码
比较时间
FPU2 < FPU < FPU1

另外,程序开始就改变RC的
会造成函数结果错误
输入小的时候测试不到
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2009-2-12 17:19:22 | 显示全部楼层
下面就是寻求执行时间少于60周期的整数算法
考虑乘法总延迟16周期
除法总延迟是50周期

所以
1、不能出现除法
2、乘法最多2个
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2009-2-12 17:26:29 | 显示全部楼层
你的FPU2的版本写成
sub edx, 1022
   mov ecx, 21
   sub ecx, edx


我的则做了优化,直接写成以下形式,实际上是等价的
  mov  ecx,1043
  sub  ecx,edx
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2009-2-12 17:28:50 | 显示全部楼层


我不理解你的做法
所以改了啊
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2009-2-12 17:52:24 | 显示全部楼层
呵呵

    在持续改进FPU2函数,增加了非跳转处理0的代码

   另外,FWAIT似乎并不必须吧
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
您需要登录后才可以回帖 登录 | 欢迎注册

本版积分规则

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

GMT+8, 2024-4-26 18:41 , Processed in 0.100902 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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