实现无限精度整数

时间:2013-02-06 22:14:05

标签: assembly integer ocaml imp imperative

问题:“一个大整数表示为(小)整数列表。”

假设有:

type reg = string;;   (* "$0" models register set to constant 0 *)
type label = string;; (* empty string models no label *)

type asmistr =
   AsmHalt
 | AsmNop
 | AsmAdd of reg * reg * reg
 | AsmAddi of reg * int * reg
 | AsmSub of reg * reg * reg
 | AsmMul of reg * reg * reg
 | AsmLoad of reg * reg * reg
 | AsmStore of reg * reg * reg
 | AsmJmp of label
 | AsmBne of reg * reg * label
 | AsmBeq of reg * reg * label
 | AsmSlt of reg * reg * reg
;;

type asmprog = AsmProg of (label * asmistr) list;;

type asmline = 
    AsmIstr of label * asmistr 
  | AsmComment of string 
  | AsmDebugReg of reg 
  | AsmDebugMem of int * int
;;

这组定义用于定义汇编语言,使用寄存器,指令和标签(用于跳转)

现在我需要从命令式语言(具有“while”“if”之类的指令)到ASM实现编译器

我老师建议的实现是使用一个列表,其中每个元素是给定数字的数字(数字可以只是整数),如11000是[1,1,0,0,0]

第一个差距是:考虑到通用的O'Caml程序,我该如何实现?假设我必须插入一个大整数,我可以使用什么逻辑来允许“计算”?因为最后,ASM程序还可以执行add,sub mul和其他可能包含大整数的指令,所以我不知道如何处理寄存器,大整数和指令

我需要的是如何实现大整数的一般方案,可能是在O'Caml语言中,以及如何考虑类似于汇编的语言(在本例中为ASM)来实现这一点

提前感谢,如果不清楚,对不起我的英语,如果有人可以帮助我,我会在需要时提供更多细节

2 个答案:

答案 0 :(得分:1)

我对你的问题的理解如下:你想编译一个简单的命令式语言,它具有无限整数的汇编,编译器将用OCaml编写。是对的吗?你的问题是"我应该如何在无界整数上编译算术运算?"。

如果这确实是个问题,那么一个好的练习就是首先在OCaml中实现那些大量的操作(使用int的列表;注意每个元素都不需要0或者1,你可以使用任何更大的基数,它的加法不会溢出你的本地OCaml整数,这将使操作更快),然后想知道如何将它移植到本机汇编程序。如何开始编辑列表?

答案 1 :(得分:0)

首先你应该明白数字有一个基础。通常人们使用十进制(基数为10),但程序员也使用十六进制(基数为16)和二进制(基数为2)。你的老师是对的(使用一个列表,其中每个元素是给定数字的数字);但可能没有提到这些数字可以在任何基础上。对于性能/效率,您应该使用base 256(其中每个数字是8位整数),或者可能使用65536(其中每个数字是16位整数),base 2 ^ 32或者base 2 ^ 64。选择取决于底层硬件预期能够处理的内容(例如,如果代码打算在16位CPU上运行,那么您将使用base 65536)。

接下来要决定的是如何存储号码。通常,您需要跟踪有多少位数,一些标志和数字列表。对于标志,您需要一个标志来指示数字是正数还是负数,但是您可以使用更多数字来表示数字是无穷大,数字是否有精确损失等等。例如,“5 /( - 6)“可能会产生”数字位数“为零的数字;设置负面和精度标志。

确定后,您需要添加正数。这主要是添加带有进位的数字,其中结果可能(最多)比最大源数字多一个数字。例如,如果您添加一个2位数字加上一个4位数字,那么您将为5位数结果分配内存,然后使用以下内容一次添加一个数字:

for(n = 0; n < result_digits; n++) {
    digit = source1[n] + source2[n] + carry;
        if(digit > DIGIT_MAX) {
            carry = 1;
            digit &= DIGIT_MASK;
        } else {
            carry = 0;
        }
        result[n] = digit;
    }
}

下一步是执行代码以处理较大正数的较小正数的减法。这只是从最高有效数字开始从进位中减去一位数。一旦这个工作,你会扩展它来处理从较小的正数减去一个较大的正数,这涉及事先交换数字并在之后否定结果。例如,“3 - 10 = - (10 - 3)”。

一旦正数的加法和减法工作,您就可以开始支持负数。这主要是摆弄标志标志并选择是否加减。例如,对于“8 +( - 3)= 8 - 3”,“8 - ( - 3)= 8 + 3”,“ - 8 + 3 = 3 - 8”,“ - 8 + -3 = - ( 8 + 3)“等。在所有情况下,它都可以重新排列,并作为正数的加法或减法来完成。

下一步是正数的乘法运算。这只是将每个数字相乘并添加前一个数字的溢出:

for(n = 0; n < result_digits; n++) {
    digit = source1[n] * source2[n] + temp;
    temp = digit >> DIGIT_BITS;
    digit &= DIGIT_MASK;
    result[n] = digit;
}

然后你会担心负数的乘法。在这里,您只需将源数字设为正数并乘以正数,然后设置结果的符号。