liangbch 发表于 2017-1-16 14:59:14

不错。使用模算术的方法,代码简洁。
我的算法仍然是普通的除法运算,很难化简到这么简单。

gxqcn 发表于 2017-1-16 17:27:25

经过变换后,问题的核心转化为计算一个 \(56\) 比特的整数对 \(2*5^9\) 的余数(最后再附加一次与 \(5^9\) 的比较运算)。
在 30# 里的 Algo_3 中,从低位到高位分成了五段(\(24|8|8|8|8\)),分别对 \(2*5^9\) 同余,最后累加后取余数。

依此原理,还可以考虑再精简一下,分成四段:\(29|9|9|9\),
即低 \(29\) 比特直接累加,而后每 \(9\) 比特依次乘以 \(1714662\)、\(2906944\)、\(74078\) 再累加(易证明, \(\sum \leqslant 2 936 365 435 \lt 2^{32}\),在 \(32\) 位下无溢出),
虽然减少了一次乘法运算,但提取数据比之前的变困难了,需要更多的位运算。

只是呼吸 发表于 2017-2-1 11:35:35

受到各位高手的刺激,加上本人对大数运算的爱好,经过深入的思考,不断的摸索,终于得到了我自己比较满意的结果。

由于站长已经把路堵死了,本人只有把站长的题目改动一下:

只用4次32位乘法,求 uint64_t 对 10^9 的余数及商。
(这里,我就不管LED灯的问题了)

程序中用到了管理员 liangbch 在 http://bbs.emath.ac.cn/thread-9245-1-1.html 中提供的“规约”技术。


我用的是vc++2008,        在debug环境下测试通过。
以下是程序的源码


#include <stdio.h>
#include <windows.h>
_declspec(naked)
int div_mod(UINT32 *dst,UINT32 *scr)
{
        _asm
        {
                push                esi
                push                edi
                push                ebx
                push                ebp
               
               
                mov                        esi,dword ptr
                mov                        ebp,dword ptr               
               
                mov                         eax,1152921504///-----这是10^(-9)的高32位
                mul                        dword ptr//64位的低位 乘 10^(-9)的高32位
               
                mov                        edi,edx       //乘积的结果,暂时保存在 edi ebx 中。
                mov                        ebx,eax

                mov                        eax,2606387915   //-------这是10^(-9)的低32位
                mul                        dword ptr// 64位的高位 乘 10^(-9)的低32位
               
                add                        ebx,eax
                adc                        edi,edx         //edi 将作为进位,ediebx存储结果
               

                mov                        eax,1152921504//-----这是10^(-9)的高32位
                mul                        dword ptr// 64位的高位 乘 10^(-9)的高32位
       
                add                        eax,edi         //把edi加进来,丢弃ebx中的低32位
                adc                        edx,0                        //eaxedx 中包含了原始64位/10^9的商,其中 edx 存储高32位,eax 存储低32位。
   
                shrd                        eax,edx,28//整体右移28位,右移方向 edx->eax->28,右移后 eax 中是商的低32位
               
                //shr                                edx,28   //得到商的高4位
                mov                                ecx,eax   //保留商的低32位
                //mov                                ebx,edx   //保留商的高32位         

                mov                        eax,1000000000
                mul                        ecx                                //ecx 商的低32位
                                                                        //这一步执行后,我们将丢弃edx中的数,只取 eax 中的值。
               
                mov                        esi,dword ptr
                mov                        edi,dword ptr//edi 原始64位数的低32位,

                sub                        edi,eax// a64 mov 10^9 的结果,保存在 edi 中

                //以下语句对 edi 中的余数和 ebx ecx 中的商进行调整,这个方法由管理员 liangbch 在http://bbs.emath.ac.cn/thread-9245-1-1.html 中提供。
                mov                        esi,1000000000
                mov                        edx,0xffffffff
                xor                        eax,eax

                cmp                        edi,esi
                SETB                        eax
                add                        edx,eax
                mov                        eax,edx//传送进位
                and                        edx,esi
                sub                        edi,edx

                mov                        dword ptr,edi//得到 a64 mod 10^9 (余数)的准确值,返回主函数

              //neg                        eax
              //add                        ecx,eax
                //adc                        ebx,0// 得到 a64/10^9 (商)的准确值,其中 ebx中存储商的高32位,ecx中存储商的低32位

                //如果需要 a64/10^9 (商) 的值,把第 38、40、65、66、67   行的注释取消即可。
                pop                        ebp
                pop                        ebx
                pop                        edi
                pop                        esi
               

                ret
        }
}






int main()
{
        UINT64 a64;
        UINT32 scr,dst;

        a64=30789000000000;
               
           scr=(UINT64)a64/4294967296;
           scr=(UINT64)a64%4294967296;
      
       div_mod(dst,scr);/// dst[] 返回的是 a64 mod 10^9 (余数)。
                                                   /// 若有需要,可以同时返回 a64/10^9 (商)。
        printf("dst=%d\n",dst);
       
        system( "pause" );

        return 0;

}
页: 1 2 3 [4]
查看完整版本: 不用64位乘除法,求 uint64_t 对 10^9 的余数