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

[讨论] 二进制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. and edx,ecx //阶码
  11. and eax,ecx //尾数
  12. shr edx,20 //得到阶段码
  13. add eax,0x100000 //设置位数最高有效位
  14. mov ecx,1043
  15. sub ecx,edx
  16. shr eax,cl //得到整数
  17. }
  18. }
  19. void testdouble2int()
  20. {
  21. double f;
  22. int n;
  23. f=23.4567;
  24. n=double2int(&f);
  25. printf("%lf=%d\n",f,n);
  26. f=23.9845;
  27. n=double2int(&f);
  28. printf("%lf=%d\n",f,n);
  29. f=2.4567;
  30. n=double2int(&f);
  31. printf("%lf=%d\n",f,n);
  32. f=2.9845;
  33. n=double2int(&f);
  34. printf("%lf=%d\n",f,n);
  35. f=65534.4567;
  36. n=double2int(&f);
  37. printf("%lf=%d\n",f,n);
  38. f=65534.9977;
  39. n=double2int(&f);
  40. printf("%lf=%d\n",f,n);
  41. f=1.00;
  42. n=double2int(&f);
  43. printf("%lf=%d\n",f,n);
  44. f=0;
  45. n=double2int(&f);
  46. printf("%lf=%d\n",f,n);
  47. }
复制代码
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-11-23 16:14 , Processed in 0.032943 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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