EVALUATING UNSIGNED INTEGER NUMERALS

Problem: Given an unsigned integer numeral in a specified base in the range from two to thirty-six, determine the integer that it denotes.

An unsigned integer numeral in a given base is a string made up entirely of digits of that base. It will be convenient to assume that the character code is ASCII, although Scheme does not require this. The digits of a base b that is in the range from to to ten are #\0 and the b - 1 characters immediately following it in the character set; if the base is greater than ten, its digits are #\0 through #\9 followed by the first b - 10 letters of the alphabet, without regard to case.

The idea of the algorithm is to traverse the numeral from left to right, computing the value of each digit of the numeral separately by calling the internally defined procedure digit-value, and adding it to an accumulator (total) of which the value is multiplied by the base on each iteration.

The procedure definition begins by introducing a sequence of local variables, which are introduced mainly for legibility; then comes the locally defined procedure, and finally the lambda-expression that denotes the procedure to be named numeral-value:

(define numeral-value
  (let ((digit-value
         (let ((zero-code (char->integer #\0))
               (nine-code (char->integer #\9))
               (capital-A-code (char->integer #\A))
               (capital-Z-code (char->integer #\Z))
               (lower-case-a-code (char->integer #\a))
               (lower-case-z-code (char->integer #\z)))

           (lambda (ch)
             (let ((code (char->integer ch)))
               (cond ((<= zero-code code nine-code)
                      (- code zero-code))
                     ((<= capital-A-code code capital-Z-code)
                      (+ 10 (- code capital-A-code)))
                     ((<= lower-case-a-code code lower-case-z-code)
                      (+ 10 (- code lower-case-a-code)))
                     (else 0)))))))

    (lambda (numeral base)
      (let ((len (string-length numeral)))
        (let loop ((total 0)
                   (index 0))
          (if (= index len)
              total
              (loop (+ (* base total)
                       (digit-value (string-ref numeral index)))
                    (+ index 1))))))))
The converse operation of constructing the numeral in a given base for a given natural number is similar in structure, except that zero must be handled as a special case:
(define numeral-for
  (let ((digit-for
         (let ((zero-code (char->integer #\0))
               (lower-case-a-code (char->integer #\a)))
           (lambda (n)
             (cond ((<= 0 n 9)
                    (integer->char (+ n zero-code)))
                   ((<= 10 n 35)
                    (integer->char (+ n lower-case-a-code -10)))
                   (else #\*))))))

    (lambda (number base)
      (if (zero? number)
          "0"
          (let loop ((chars '())
                     (rest number))
            (if (zero? rest)
                (apply string chars)
                (loop (cons (digit-for (remainder rest base)) chars)
                      (quotient rest base))))))))
The composition of these procedures converts a numeral from one base to another:
(define base-convert
  (lambda (numeral input-base output-base)
    (numeral-for (numeral-value numeral input-base) output-base)))


This document is available on the World Wide Web as

http://www.math.grin.edu/~stone/events/scheme-workshop/numerals.html


created July 9, 1995
last revised June 24, 1996

John David Stone (stone@math.grin.edu)