ITEEDU

用汇编计算圆周率

概述:

用汇编语言编制计算程序并不是强项,特别是在涉及到浮点计算时,但汇编的一个好处就是速度快,所以在整数计算时可以试一下。本文的理论基础来自是电脑杂志1996年第10期,作者郭继展发表的一篇文章,作者提出一个公式:
PI=16arctg(1/5)-4arctg(1/239)
在展开成两个级数之和,然后整理得到:
PI=16x(1/5-1/(5^3/3)+1/(5^5/5)-1/(5^7/7)+...)-4x(1/239-1/(239^3/3)+1/(239^5/5)-1/(239^7/7)+...)
=4x(4x5/25-239/57121)/1-4x(4x5/25^2-239/57121^2)/3+4x(4x5/25^3-239/57121^3)/5-...
我对以上公式和推导一看就头疼,但根据它编出的程序却可以在4分钟内算出圆周率的小数点下8万位!(在P5/200上)想当年祖冲之算了一生才算到3.14159265,十九世纪英国人香克思用了一生才算到小数点下707位。
本程序的难点就是如何达到小数点下这么多位的精度,这个办法就是:在计算机中一个 WORD 可以表示0到65535,我们可以在内存定义一个字来表示五位数,如果要算到小数点下10000位,则定义2000个字来表示它,如计算239/57121时,可以用23900000/57121,得到小于五位的结果存到第一个字中,然后用余数乘以100000再除57121,得到小于五位的结果存到第二个字中,依此类推。为了计算时不至于溢出,本程序动用一个双字来表示五位数,再用一个段64K来表示一个高精度数,共可以表示(65536/4)*5 共有小数点下 81920 位。一共用到三个段,第一个段存储(4*4*5/25^n),第二个段存储(4*239/57121^n),第三个段存储最后的结果即 PI。
本程序的意义就在于提出了一个表示高精度数的办法,如果内寸足够的话,理论上可以进行任何精度的计算。这里是编译好的可执行文件:pi.com,计算结果8万位的PI值:pi.txt,还有本程序要用到的两个公用子程序 scanf.asm 和 printf.asm,用于读入键盘数字输入和屏幕数字输出。(引用本程序时请注明出处,并请勿改动版权信息)

源程序:

.386
        CODE  SEGMENT   USE16
              ASSUME    CS:CODE,DS:CODE
              ORG       100H
      START:
              JMP       INSTALL

      HANDLE  DW        ?
        _MIN  DW        ?
        _SEC  DW        ?
       _SEC1  DW        ?
          _A  DD        ?
          _B  DD        ?
   _FS_POINT  DW        0
   _GS_POINT  DW        0
        _DIV  DD        1
        FLAG  DB        ?           ;=1 +
     DIGITAL  DD        5000        ;how many points want to calculate
       POINT  DW        ?           ;total point /5
   NUM_POINT  DW        ?           ;total point /5 * 4
      _COUNT  DD        ?
    TMP_NUM0  DD        ?
     TMP_NUM  DD        10 DUP (?)
    KEY_BUFF  DB        6,0,6 DUP (0)

      D_SAVE  DB        'Saving result to file %c ...',0
              DW        80H
   D_SAVE_OK  DB        8,8,8,8,', OK !',0DH,0AH,0
  D_SAVE_ERR  DB        8,8,8,8,', error !',0DH,0AH,0

      D_TIME  DB        0DH,0AH,'Total %ld points, time used %d:%02d.%03d, '
              DB        'calculate %ld times.',0DH,0AH,0
              DW        DIGITAL,_MIN,_SEC,_SEC1,_DIV
      D_SCAN  DB        '<> Dec 18, 1996',0DH,0AH
              DB        'Copyright(C) by Luo Yun Bin, phone 0576-4114689',0DH,0AH,0AH
              DB        'How many points (10-80000): ',0
     D_ABORT  DB        'User pressed Esc, calculate aborted !%20r ',0DH,0AH,0
       D_CAL  DB        'Calculating, please waiting ... (Esc to cancel)',0DH,0
      D_CAL1  DB        '%d %% calculated, please waiting ... (Esc to cancel)',0DH,0
              DW        PERCENT
     PERCENT  DW        ?
      D_STR1  DB        ' PI = %1ld.%c',0DH,0AH,0
              DW        TMP_NUM0,D_SUB_STR
      D_STR2  DB        '%5ld : %c'
              DB        0DH,0AH,0
              DW        _COUNT,D_SUB_STR
   D_SUB_STR  DB        '%05ld %05ld %05ld %05ld %05ld '
              DB        '%05ld %05ld %05ld %05ld %05ld',0
              DW        TMP_NUM,TMP_NUM+4,TMP_NUM+8,TMP_NUM+12,TMP_NUM+16
              DW        TMP_NUM+20,TMP_NUM+24,TMP_NUM+28,TMP_NUM+32,TMP_NUM+36

    INSTALL:
              MOV       SI,OFFSET D_SCAN
              CALL      PRINTF
              MOV       AH,0AH
              MOV       DX,OFFSET KEY_BUFF
              INT       21H
              MOV       SI,OFFSET KEY_BUFF+2
              CALL      SCANF
              MOV       EAX,DWORD PTR SCAN_NUM
              MOV       DIGITAL,EAX
              MOV       SI,OFFSET D_CAL
              CALL      PRINTF

              XOR       AX,AX
              MOV       DS,AX
              MOV       AX,DS:[046CH]
              PUSH      CS
              POP       DS
              MOV       _SEC,AX

              MOV       AX,CS
              ADD       AX,1000H    ;result of 4*4*5/25^n
              MOV       FS,AX
              ADD       AX,1000H    ;result of 4*239/57121^n
              MOV       GS,AX
              ADD       AX,1000H    ;total result
              MOV       BP,AX

              MOV       AX,FS
              CALL      INIT_NUM
              MOV       DWORD PTR FS:[4],4*239*100000

              MOV       AX,GS
              CALL      INIT_NUM
              MOV       DWORD PTR GS:[4],4*4*5*100000

              MOV       AX,BP
              CALL      INIT_NUM

              CALL      PRE
              CALL      CALC

              XOR       AX,AX
              MOV       DS,AX
              MOV       AX,DS:[046CH]
              PUSH      CS
              POP       DS
              MOV       _SEC1,AX

              PUSH      POINT
              CALL      NUM_OUT
              POP       POINT

              MOV       AX,_SEC1
              SUB       AX,_SEC
              MOV       CX,55
              MUL       CX
              MOV       CX,1000
              DIV       CX
              MOV       _SEC1,DX
              MOV       CX,60
              XOR       DX,DX
              DIV       CX
              MOV       _MIN,AX
              MOV       _SEC,DX
              MOV       SI,OFFSET D_TIME
              CALL      PRINTF

              MOV       SI,81H
              MOV       DI,80H
    CMD_LOP:
              LODSB
              CMP       AL,0DH
              JZ        CMD_END
              CMP       AL,20H
              JBE       CMD_LOP
              CMP       AL,'a'
              JB        CMD_STORE
              CMP       AL,'z'
              JA        CMD_STORE
              SUB       AL,20H
  CMD_STORE:
              STOSB
              JMP       SHORT CMD_LOP
    CMD_END:
              XOR       AL,AL
              STOSB

              MOV       SI,80H
              CMP       BYTE PTR DS:[SI],0
              JZ        QUIT

              MOV       SI,OFFSET D_SAVE
              CALL      PRINTF

              MOV       AH,3CH
              XOR       CX,CX
              MOV       DX,80H
              INT       21H
              JB        FILE_ERR

              MOV       HANDLE,AX
              MOV       STD_OUT,OFFSET WRITE_FILE
              CALL      NUM_OUT

              MOV       STD_OUT,OFFSET PRT_TO_SCR
              MOV       SI,OFFSET D_SAVE_OK
              CALL      PRINTF

              MOV       AH,3EH
              MOV       BX,HANDLE
              INT       21H
              INT       20H
   FILE_ERR:
              MOV       SI,OFFSET D_SAVE_ERR
              CALL      PRINTF
              INT       20H
       QUIT:
              INT       20H

  WRITE_FILE  PROC

              PUSHA
              MOV       AH,40H
              MOV       FLAG,AL
              MOV       BX,HANDLE
              MOV       CX,1
              MOV       DX,OFFSET FLAG
              INT       21H
              POPA
              RET

  WRITE_FILE  ENDP

         PRE  PROC

              MOV       EAX,DIGITAL ;total 65536*5/4 points
              CMP       EAX,(65536/4-3)*5       ;comp max points
              JBE       PRE_1
              MOV       EAX,(65536/4-3)*5
      PRE_1:
              CMP       EAX,10      ;comp min points
              JAE       PRE_2       ;must > 10 and < 81915
              MOV       EAX,10
      PRE_2:
              XOR       EDX,EDX
              MOV       ECX,5
              DIV       ECX
              MOV       EBX,EAX
              INC       EBX
              MOV       POINT,BX    ;point for print control

              MUL       ECX
              MOV       DIGITAL,EAX ;

              MOV       EAX,EBX
              INC       EAX
              MOV       ECX,4
              MUL       ECX
              MOV       NUM_POINT,AX            ;max used memory

              RET

         PRE  ENDP


        CALC  PROC

              MOV       ES,BP
     C_LOP0:
              MOV       AH,1
              INT       16H
              JZ        CALC_0
              XOR       AH,AH
              INT       16H
              CMP       AL,1BH
              JNZ       CALC_00
              PUSH      CS
              POP       ES
              MOV       SI,OFFSET D_ABORT
              CALL      PRINTF
              INT       20H
    CALC_00:
              XOR       EAX,EAX
              MOV       AX,_GS_POINT
              MOV       ECX,500
              MUL       ECX
              MOV       ECX,4
              DIV       ECX
              XOR       EDX,EDX
              DIV       DIGITAL
              MOV       PERCENT,AX
              MOV       SI,OFFSET D_CAL1
              PUSH      CS
              POP       ES
              CALL      PRINTF
              MOV       ES,BP
     CALC_0:
              XOR       EAX,EAX
              MOV       ECX,100000
              MOV       _A,EAX
              MOV       _B,EAX
              XOR       FLAG,00000001B          ;init part
;============================================================
              MOV       EBX,57121
     C_LOP1:
              MOV       SI,_FS_POINT            ;if 4*5/25^n = 0 skip
              CMP       SI,NUM_POINT
              JAE       CALC_1
              CMP       DWORD PTR FS:[SI],0
              JNZ       C_LOP2
              ADD       _FS_POINT,4
              JMP       SHORT C_LOP1
     C_LOP2:
              MUL       ECX
              ADD       EAX,FS:[SI]
              ADC       EDX,0
              DIV       EBX
              MOV       FS:[SI],EAX
              MOV       EAX,EDX

              ADD       SI,4
              CMP       SI,NUM_POINT
              JB        C_LOP2
;=======================================================
     CALC_1:
              MOV       EBX,25
     C_LOP3:
              MOV       SI,_GS_POINT            ;if 4*5/25^n = 0 skip
              CMP       SI,NUM_POINT
              JAE       CALC_4
              CMP       DWORD PTR GS:[SI],0
              JNZ       C_LOP4
              ADD       _GS_POINT,4
              JMP       SHORT C_LOP3
     C_LOP4:
              MOV       EAX,_A
              MUL       ECX
              ADD       EAX,GS:[SI]
              ADC       EDX,0
              DIV       EBX
              MOV       GS:[SI],EAX
              MOV       _A,EDX

              MOV       EAX,_B
              MUL       ECX
              ADD       EAX,GS:[SI]
              ADC       EDX,0
              SUB       EAX,FS:[SI]
              SBB       EDX,0

              CMP       EDX,0
              JL        _T1
              DIV       _DIV
              MOV       _B,EDX
              JMP       SHORT _T2
        _T1:
              IDIV      _DIV
              DEC       EAX
              ADD       EDX,_DIV
              MOV       _B,EDX
        _T2:
              TEST      FLAG,00000001B
              JNZ       CALC_2
              SUB       ES:[SI],EAX
              JMP       SHORT CALC_3
     CALC_2:
              ADD       ES:[SI],EAX
     CALC_3:
              ADD       SI,4
              CMP       SI,NUM_POINT
              JB        C_LOP4
     C_LOP5:
              ADD       _DIV,2
              JMP       C_LOP0
;====================================================
     CALC_4:
              XOR       EAX,EAX
              MOV       _A,EAX
              MOV       SI,NUM_POINT
     C_LOP6:
              SUB       SI,4
              MOV       EAX,ES:[SI]
              ADD       EAX,_A

              CMP       EAX,ECX
              JGE       CALC_5
              CMP       EAX,0
              JLE       CALC_6
              MOV       _A,0
              MOV       ES:[SI],EAX
              JMP       SHORT CALC_8
     CALC_5:
              XOR       EDX,EDX
              DIV       ECX
              MOV       ES:[SI],EDX
              MOV       _A,EAX
              JMP       SHORT CALC_8
     CALC_6:
              MOV       EDX,-1
              IDIV      ECX
              DEC       EAX
              ADD       EDX,ECX
              MOV       ES:[SI],EDX
              MOV       _A,EAX
     CALC_8:
              CMP       SI,4
              JA        C_LOP6

              MOV       EAX,_A
              MOV       ES:[0],EAX

              PUSH      CS
              POP       ES
              RET

        CALC  ENDP

    INIT_NUM  PROC

              PUSH      ES
              MOV       ES,AX
              XOR       EAX,EAX
              CLD
              XOR       DI,DI
              MOV       CX,65536/4
              CLD
              REP       STOSD
              POP       ES
              RET

    INIT_NUM  ENDP

     NUM_OUT  PROC

              CLD
              XOR       ESI,ESI
              MOV       _COUNT,ESI
              MOV       DI,OFFSET TMP_NUM0
              MOV       CX,11
              CMP       CX,POINT
              JBE       NO_ADJUST
              MOV       CX,POINT
  NO_ADJUST:
              SUB       POINT,CX
              MOV       DS,BP
              REP       MOVSD
              PUSH      CS
              POP       DS

              PUSH      SI
              MOV       SI,OFFSET D_STR1
              CALL      PRINTF
              POP       SI
     NO_LOP:
              CMP       POINT,0
              JLE       NO_QUIT
              MOV       CX,10
              MOV       DI,OFFSET TMP_NUM
              XOR       EAX,EAX
              REP       STOSD

              MOV       CX,10
              MOV       DI,OFFSET TMP_NUM
              CMP       POINT,10
              JAE       NO1
              MOV       CX,POINT
        NO1:
              SUB       POINT,CX
              ADD       _COUNT,50
              MOV       DS,BP       ;bp = result segment
              REP       MOVSD
              PUSH      CS
              POP       DS

              PUSH      SI
              MOV       SI,OFFSET D_STR2
              CALL      PRINTF
              POP       SI
              JMP       SHORT NO_LOP
    NO_QUIT:
              RET

     NUM_OUT  ENDP

     INCLUDE  SCANF.ASM
     INCLUDE  PRINTF.ASM

        CODE  ENDS
              END       START