2006年2月14日星期二

怎样显示一个整数

原贴:http://tctianchi.yo2.cn/articles/integer.html

在这个汇编也能调用wprintf的时代讨论这个问题是不是太过时了?如果变量a中保存有十六进制数0FFF FFFF,无论是printf("%d",a);、
Print a、writeln(a);,还是echo a;、Response.Write(a),都能显示它的十进制版本。

将寄存器中的数H:L转化为十进制数显示在屏幕上的过程可归约为以下迭代过程:
1、该十进制数的末位是H:L除以10的余数
2、该十进制数的其余各位是H:L除以10的商形成的新数,应用第1-2条所述规则的结果,直到新数为0
但是仔细想想却不是这样简单的。div指令规定:保存在DX:AX中的32位整数除以16位整数时,商将保存在AX余数在DX。因此如果除数为10,被除数大于9FFFF的话,将发生除法溢出。


研究发现,上面的迭代过程可简化为:
[结论]
1、设x为32位整数,其高位为H除以10的模,低位为L
2、该十进制数的末位是x除以10的余数
3、该十进制数的其余各位是新数y,应用第1-4条所述规则的结果,直到y为0
4、新数y为32位整数,高位为H除以10的商,低位为x除以10的商


[证明]
以下符号中,\为整除,Mod为求模,^为求幂。设H、L为16位整数,S=16^4=2^16。
则H:L=H*S+L,x Mod y = x - (x \ y) * y。
还可推知:x \ y = (x - x Mod y) / y。
性质1:a \ b \ c = a \ c \ b = a \ (b * c),即整除具有交换率和结合率,但没有分配率。需要分配时,应转换为除法。
性质2:(a + b) Mod c = ((a Mod c) + (b Mod c)) Mod c,证明略。


结论2的证明:(H * S + L) Mod 10 = ((H Mod 10) * S + L) Mod 10
((H Mod 10) * S + L) Mod 10
= ((H - (H \ 10) * 10) * S + L) Mod 10
= ((H * S + L) Mod 10 - ((H \ 10) * 10 * S) Mod 10) Mod 10
∵(H \ 10) * 10 * S) Mod 10
= (H \ 10) * S * 10) Mod 10
= 0
∴原式
= ((H * S + L) Mod 10) Mod 10
= (H * S + L) Mod 10


结论4的证明:(H * S + L) \ 10 = ((H Mod 10) * S + L) \ 10
(H * S + L) \ 10
重新分配高、低16位
= S * ( _
((H * S + L) \ 10) \ S _
) + ( _
((H * S + L) \ 10) Mod S _
)
其中高位 ((H * S + L) \ 10) \ S
= (H * S + L) \ S \ 10
= H \ 10
其中低位 ((H * S + L) \ 10) Mod S
= (H * S + L) \ 10 - (H * S + L) \ 10 \ S * S
= (H * S + L) \ 10 - H \ 10 * S
= (H * S + L - (H * S + L) Mod 10) / 10 - (H - H Mod 10) / 10 * S
= (L - (H * S + L) Mod 10 + (H Mod 10) * S) / 10
∵(H * S + L) Mod 10 = ((H Mod 10) * S + L) Mod 10 ( 结论2 )
∴原式
= ((H Mod 10) * S + L - ((H Mod 10) * S + L) Mod 10) / 10
= ((H Mod 10) * S + L) \ 10
证毕。


; 显示DX:AX中的有符号长整数,
; 转换后的字符串保存在[BX]中
; [tc]天驰2006.2......


;显示整数
TCPRINTCLNG PROC NEAR
PUSH AX
PUSH BX
PUSH CX
PUSH DX
PUSH SI


XOR CX,CX ; CX是计数器
MOV SI,10D
MOV DI,BX
ADD DI,11D

MOV BX,DX
OR BX,BX
; 测负数
JNS TCPRINTLNGLABELCOVERTPRE
NEG BX
NEG AX
SBB BX,+00
CALL TCPRINTLNGLABELCOVERT
DEC DI
; 加一个'-'
MOV BYTE PTR [DI],2DH
INC CX
JMP TCPRINTLNGLABELOUT


TCPRINTLNGLABELCOVERTPRE:
CALL TCPRINTLNGLABELCOVERT
JMP TCPRINTLNGLABELOUT


TCPRINTLNGLABELCOVERT: ; 转换
XOR DX,DX
XCHG BX,AX
DIV SI ; 高位除法
XCHG BX,AX
DIV SI ; 低位除法
ADD DL,30H
CMP DL,3AH ; 个位比'9'大
JB TCPRINTLNGLABELHEX
ADD DL,07H ; 就变成'A'

TCPRINTLNGLABELHEX:
INC CX ; 得到位数
; 保存
DEC DI
MOV BYTE PTR [DI],DL
MOV DX,AX
OR DX,BX
JNZ TCPRINTLNGLABELCOVERTRET


; 正式显示,CX是个数
TCPRINTLNGLABELOUT:
MOV DL,[DI]
INC DI
MOV AH,02H
INT 21H
LOOP TCPRINTLNGLABELOUT


MOV DL,13D ;回车1个
MOV AX,0200H
INT 21H
MOV DL,10D
MOV AX,0200H
INT 21H


POP SI
POP DX
POP CX
POP BX
POP AX
RET

TCPRINTCLNG ENDP


这么冗长的代码本来不想贴的,真的。但我为了这个问题思考了一个晚上,不能白费啊。

这种六七十年代就可能被发明的东西,为什么一直没有人记录下来呢?害得我自己伤脑细胞。

没有评论: