Infinite precision arithmetic
Full implementations of Common Lisp include arbitrary-precision integer arithmetic, so you can add, subtract, multiply, and divide integers as large as you like. The arithmetic in uLisp is limited to 16 bits, giving a range of 32767 to -32768, but this example shows how you can implement infinite-precision integer arithmetic in uLisp with a few simple functions.
These routines require too much memory for an Arduino Uno or ATmega328, but will run happily on most other 8/16-bit platforms or 32-bit platforms.
For an extension to uLisp that provides a full arbitrary precision arithmetic library see Arbitrary-precision arithmetic extension.
Representing big numbers
Each big number is represented as a list of small numbers in the range 0 to 99, with the least-significant digits at the start of the list, so the number 7654321 would be represented as the list:
(21 43 65 7)
The function big converts a uLisp integer into big number notation:
(defun big (n) (cond ((< n 100) (list n)) (t (cons (mod n 100) (big (truncate n 100))))))
Here's an example:
> (big 32767) (67 27 3)
On 32-bit platforms it would be more efficient to use a list of numbers from 0 to 9999; this is left as an exercise for the reader.
Printing big numbers
The function pri uses format to print each pair of digits in the list representing a number in big notation:
(defun pri (n) (let ((rn (reverse n))) (format t "~d~{~2,'0d~}" (car rn) (cdr rn))))
The first pair of digits is printed with any leading zero suppressed.
Adding big numbers
The function add adds together two numbers in big notation:
(defun add (a b) (cond ((null a) b) ((null b) a) (t (put (+ (car a) (car b)) (add (cdr a) (cdr b))))))
It uses the following function, put, which adds an integer n to a big number c with carry:
(defun put (n c) (cond ((< n 100) (cons n c)) (t (cons (mod n 100) (add (list (truncate n 100)) c)))))
Here's the calculation of 654321 + 987654 = 1641975:
> (add '(21 43 65) '(54 76 98)) (75 19 64 1)
Multiplying big numbers
To multiply two big numbers we use the fact that:
(a + 100b) * (c + 100d) = ac + 100 * (bc + ad) + 10000 * bd.
To multiply a big number b by 100 we simply need to do:
(cons 0 b)
Here's the full definition of mul which multiplies two numbers in big notation:
(defun mul (a b) (cond ((null a) nil) ((null b) nil) (t (add (list (* (car a) (car b))) (cons 0 (add (add (mul (list (car a)) (cdr b)) (mul (cdr a) (list (car b)))) (let ((c (mul (cdr a) (cdr b)))) (when c (cons 0 c)))))))))
Here's the calculation of 654321 * 987654 = 646242752934:
> (mul '(21 43 65) '(54 76 98)) (34 29 75 42 62 64)
Calculating factorials
As an example of using the infinite precision package, here's a routine to calculate the factorial of a number n:
(defun fac (n) (let ((f (big 1))) (dotimes (x n) (setq f (mul f (big (1+ x))))) (pri f) (print nothing)))
To calculate factorial 24:
> (fac 24) 620448401733239439360000
Integer exponents
Here's a second example, which will find the value of x^y for integer x and y. It uses binary exponentiation, which drastically reduces the number of multiplications needed:
(defun iex (x y) (let ((e (big 1)) (f (big x))) (loop (when (zerop y) (return)) (when (oddp y) (setq e (mul e f))) (setq f (mul f f) y (ash y -1))) (pri e) (print nothing)))
For example:
> (iex 2 64) 18446744073709551616
Here's the whole infinite precision package: Infinite precision package.
This will work on both the integer versions of uLisp, and the 32-bit versions of uLisp, which include floating-point support.