ulldiv实现

来源:互联网 发布:windows与linux双系统 编辑:程序博客网 时间:2024/04/21 00:19

title   ulldiv - unsigned long divide routine
;***
;ulldiv.asm - unsigned long divide routine
;
;       Copyright (c) 1985-1997, Microsoft Corporation. All rights reserved.
;
;Purpose:
;       defines the unsigned long divide routine
;           __aulldiv
;
;*******************************************************************************


.xlist
include cruntime.inc
include mm.inc
.list

;***
;ulldiv - unsigned long divide
;
;Purpose:
;       Does a unsigned long divide of the arguments.  Arguments are
;       not changed.
;
;Entry:
;       Arguments are passed on the stack:
;               1st pushed: divisor (QWORD)
;               2nd pushed: dividend (QWORD)
;
;Exit:
;       EDX:EAX contains the quotient (dividend/divisor)
;       NOTE: this routine removes the parameters from the stack.
;
;Uses:
;       ECX
;
;Exceptions:
;
;*******************************************************************************

        CODESEG

_aulldiv        PROC NEAR

        push    ebx
        push    esi

; Set up the local stack and save the index registers.  When this is done
; the stack frame will look as follows (assuming that the expression a/b will
; generate a call to uldiv(a, b)):
;
;               -----------------
;               |               |
;               |---------------|
;               |               |
;               |--divisor (b)--|
;               |               |
;               |---------------|
;               |               |
;               |--dividend (a)-|
;               |               |
;               |---------------|
;               | return addr** |
;               |---------------|
;               |      EBX      |
;               |---------------|
;       ESP---->|      ESI      |
;               -----------------
;

DVND    equ     [esp + 12]      ; stack address of dividend (a)
DVSR    equ     [esp + 20]      ; stack address of divisor (b)

;
; Now do the divide.  First look to see if the divisor is less than 4194304K.
; If so, then we can use a simple algorithm with word divides, otherwise
; things get a little more complex.
;

        mov     eax,HIWORD(DVSR) ; check to see if divisor < 4194304K
        or      eax,eax
        jnz     short L1        ; nope, gotta do this the hard way
        mov     ecx,LOWORD(DVSR) ; load divisor
        mov     eax,HIWORD(DVND) ; load high word of dividend
        xor     edx,edx
        div     ecx             ; get high order bits of quotient
        mov     ebx,eax         ; save high bits of quotient
        mov     eax,LOWORD(DVND) ; edx:eax <- remainder:lo word of dividend
        div     ecx             ; get low order bits of quotient
        mov     edx,ebx         ; edx:eax <- quotient hi:quotient lo
        jmp     short L2        ; restore stack and return

;
; Here we do it the hard way.  Remember, eax contains DVSRHI
;

L1:
        mov     ecx,eax         ; ecx:ebx <- divisor
        mov     ebx,LOWORD(DVSR)
        mov     edx,HIWORD(DVND) ; edx:eax <- dividend
        mov     eax,LOWORD(DVND)
L3:
        shr     ecx,1           ; shift divisor right one bit; hi bit <- 0
        rcr     ebx,1
        shr     edx,1           ; shift dividend right one bit; hi bit <- 0
        rcr     eax,1
        or      ecx,ecx
        jnz     short L3        ; loop until divisor < 4194304K
        div     ebx             ; now divide, ignore remainder
        mov     esi,eax         ; save quotient

;
; We may be off by one, so to check, we will multiply the quotient
; by the divisor and check the result against the orignal dividend
; Note that we must also check for overflow, which can occur if the
; dividend is close to 2**64 and the quotient is off by 1.
;

        mul     dword ptr HIWORD(DVSR) ; QUOT * HIWORD(DVSR)
        mov     ecx,eax
        mov     eax,LOWORD(DVSR)
        mul     esi             ; QUOT * LOWORD(DVSR)
        add     edx,ecx         ; EDX:EAX = QUOT * DVSR
        jc      short L4        ; carry means Quotient is off by 1

;
; do long compare here between original dividend and the result of the
; multiply in edx:eax.  If original is larger or equal, we are ok, otherwise
; subtract one (1) from the quotient.
;

        cmp     edx,HIWORD(DVND) ; compare hi words of result and original
        ja      short L4        ; if result > original, do subtract
        jb      short L5        ; if result < original, we are ok
        cmp     eax,LOWORD(DVND) ; hi words are equal, compare lo words
        jbe     short L5        ; if less or equal we are ok, else subtract
L4:
        dec     esi             ; subtract 1 from quotient
L5:
        xor     edx,edx         ; edx:eax <- quotient
        mov     eax,esi

;
; Just the cleanup left to do.  edx:eax contains the quotient.
; Restore the saved registers and return.
;

L2:

        pop     esi
        pop     ebx

        ret     16

_aulldiv        ENDP
 

求助!! 关于ulldiv的实现   
pigsanddogs
我爱吃猪肉,但是长不胖,为什么?? 
发表于:2007-08-31 18:05:36 楼主
对于64位无符号除法, vc调用一个库函数_ulldiv来实现,
原代码在rtl/intel/ulldiv.asm有实现,简单描述成c语言就是这样的
假设a,b,c都是64位数, 那么要求c = a/b

a ' = a;
b ' = b;

if (b '  < 0x100000000)
{
    return a '/b ';   //因为如果b能用一个dword表示, 那么可以edx:eax div ecx实现
}

while(b  >= 0x10000000)  //否则, 就把b和a同时右移, 移动到b能放到一个dword结束
{
   b '  > >= 2;
   a '  > >= 2;
}
c = a '/b ';           // 这个时候,再次可以用edx:eax div ecx来实现.
if (c*b  > a) --c;    // 如果c*b比a要大的话, 说明刚得到的c比实际的c要少一.
return c;


其实算法很巧妙, 而且人也非常容易理解.
我把2个数都缩小一点, 然后来除,  
但问题是: 
怎么证明这个都缩小以后算出来的c就比原来的c大1, 或者跟原来的c一样的大?


ps: 这个问题困惑了我1个月, 天天晚上就是想办法证明, 
但总是做不出来. 现在睡觉都睡不好了. 帮我解决下, 另外我在送100分.

 
 
 
问题点数:30 回复次数:5  显示所有回复显示星级回复显示楼主回复    
 

galois_godel
 
等 级:
 发表于:2007-09-01 14:12:431楼 得分:0
对吗?
a=2^63=9223372036854775808
b=2^32+3=4294967299
c=a/b=2147483646

b '=b > >2=1073741824
a '=a > >2=2305843009213693952

c '=a '/b '=2147483648

相差2拉
 
  
 
galois_godel
 
等 级:
 发表于:2007-09-01 14:14:392楼 得分:0
如果a <=2^60 可以证明是正确的
如果每次只一位的话,a <=2^62时也是对的
 
  
 
galois_godel
 
等 级:
 发表于:2007-09-01 14:20:063楼 得分:0
每次只移一位的话,a <=2^64 都是对的,也就说就对了
 
  
 
galois_godel
 
等 级:
 发表于:2007-09-01 14:34:134楼 得分:0
  shr     ecx,1           ; shift divisor right one bit; hi bit  <- 0
        rcr     ebx,1
        shr     edx,1           ; shift dividend right one bit; hi bit  <- 0
        rcr     eax,1
好像就是一位一位移的阿
 
  
 
galois_godel
 
等 级:
 发表于:2007-09-01 14:46:365楼 得分:0
证明如下:

c=a/b=(a 'N+a0)/(b 'N+b0) <=(a 'N+N-1)/(b 'N)=(a '+(N-1)/N)/b '=c '

c=a/b=(a 'N+a0)/(b 'N+b0) >=(a 'N)/(b 'N+N-1)=c '/(1+(N-1)/(b 'N))
c+c*(N-1)/(b 'N) >=c ' (1)

注意到b ' >=2^31

如果b >=2^33
c=a/b <=2^64/2^33=2^31 <=b '

c*(N-1)/(b 'N) <=1  则 c+1 >=c ' (2)

如果b <2^33 那么移一位就可以了,也就是N=2
c=a/b <=2^64/2^32=2^32
c*(N-1)/(b 'N)=c/(2b ') <=2^32/(2*2^31)=1
也可以得出 c+1 >=c ' (2)

综合 (1),(2), c= <c ' <=c+1

也是 把c '=c, 或者 c '=c+1 就可以了

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 孩子长倒睫毛该怎么办 一年级的孩子口算慢怎么办? 大班社会领域怎么办教学反思 表带活动圈坏了怎么办 手表固针h掉了怎么办 cad图全部倒过来了怎么办 电脑打数字变粗怎么办 wps表格输入数字变乱码怎么办 文档中光标变粗怎么办 wps排序有重复的怎么办 记英语数字老是混怎么办 苹果手机输入法出数字怎么办 word文档复制过来有底色怎么办 表格复制后还再显示复制符号怎么办 笔记本字母键打出数字怎么办 wps打字字上移了怎么办 微信里黑圈里面的白字是怎么办 脖子比脸黑好多怎么办 河南许昌小学生生病办休学怎么办 1岁宝宝内向胆小怎么办 3岁宝宝内向胆小怎么办 数学物理好不喜欢学医怎么办 孩子眉毛太浓了怎么办 小娃一年级成绩太差怎么办 孩子晚上睡得晚怎么办 在深圳买房难小孩读书怎么办 上海小孩读书积分不够怎么办 读书时静不下心怎么办 初中生静不下心读书怎么办 孩子学习静不下心怎么办 退烧后体温35度怎么办 孩子体温34度多怎么办 宝宝感冒咳嗽流鼻涕出汗怎么办 养的小鸡总打架怎么办 小鸡一条腿瘸了怎么办 同窝小斗鸡打架怎么办 夏季羊长的慢怎么办 1岁吃母乳不吃饭怎么办 5个月宝宝黏妈妈怎么办 九个月宝宝不爱吃饭怎么办 20个月宝宝吐了怎么办