Big Numbers: Input And Output
June 14, 2011
These functions are straight forward, and assume that the radix is less than or equal to big-base:
(define (string->big str . args)
(define (char->digit c)
(cond ((char-numeric? c) (- (char->integer c) 48))
((char-upper-case? c) (- (char->integer c) 55))
((char-lower-case? c) (- (char->integer c) 87))
(else (error 'char->digit "illegal character"))))
(if (string=? str "") '(0)
(let* ((radix (list 1 (if (null? args) 10 (car args))))
(s (if (char=? (string-ref str 0) #\-) -1 1))
(ds (string->list str))
(ds (if (positive? s) ds (cdr ds)))
(ds (map char->digit ds)))
(let loop ((ds (cdr ds)) (big (list 1 (car ds))))
(if (null? ds) (cons (* s (car big)) (cdr big))
(loop (cdr ds)
(big-plus (big-times big radix)
(list 1 (car ds)))))))))
(define (big->string big . args)
(define (digit->char d)
(cond ((< d 10) (integer->char (+ d 48)))
((< d 36) (integer->char (+ d 55)))
(else (error 'digit->char "illegal digit"))))
(if (big-zero? big) "0"
(if (big-negative? big) (string-append "-" (big->string (big-negate big)))
(let ((radix (list 1 (if (null? args) 10 (car args)))))
(let loop ((big big) (ds '()))
(if (big-zero? big) (list->string ds)
(call-with-values
(lambda () (big-divide big radix))
(lambda (q r) (loop q (cons (digit->char (cadr r)) ds))))))))))
Testing these functions turned up a big in big-divide: when the quotient was zero, it was represented as (1 0) instead of the correct representation (0).
The complete big-number library to this point is shown on the next page. You can run the program at http://programmingpraxis.codepad.org/4TvtUDo3.
My Haskell solution (see http://bonsaicode.wordpress.com/2011/06/14/programming-praxis-big-numbers-input-and-output/ for a version with comments):
readBase :: (Num a, Enum a) => a -> String -> a readBase b ('-':xs) = - readBase b xs readBase b xs = foldl (\a x -> b * a + val x) 0 xs where val d = maybe (error "unrecognized digit") id . lookup d $ zip (['0'..'9'] ++ ['A'..'Z'] ++ ['a'..'z']) [0..] showBase :: Integral a => a -> a -> String showBase b n = if n < 0 then '-' : showBase b (abs n) else map (digit . snd) $ m : reverse ms where ((_:ms), (m:_)) = span ((> 0) . fst) $ iterate (flip divMod b . fst) (n, 0) digit d = maybe undefined id . lookup d . zip [0..] $ ['0'..'9'] ++ ['A'..'Z'] ++ ['a'..'z']