round.ss
#lang scheme

;; returns the closest number to X that has exactly DIGITS significant
;; figures.
(define (my-round x digits)

  ;; Returns a representation of x in scientific notation.  The
  ;; resulting values are the mantissa and the exponent.  So, for
  ;; example, (scientific 27) => (values 2.7 1), meaning that 27 is equal
  ;; to 2.7 times 10 ^ 1.

  (define (scientific x)
    (let loop ((mantissa x)
               (exponent 0))
      (if (or (zero? x)
              (and (>= (abs mantissa) 1)
                   (< (abs mantissa) 10)))
          (values mantissa exponent)
          (if (>= (abs mantissa) 10)
              (loop (/ mantissa 10)
                    (+ exponent 1))
              (loop (* mantissa 10)
                    (- exponent 1))))))

  (define (eggzackly x)
    (if (exact? x)
        x
      (inexact->exact x)))

  (let-values ([(mantissa exponent) (scientific x)])
    (* (eggzackly (round
                   (* mantissa (expt 10 (- digits 1)))))

       ;; You might be tempted to call `inexact->exact' on the return
       ;; from `expt' here, but that would be a mistake, because some
       ;; Schemes (such as Guile 1.3) don't have exact non-integers --
       ;; so that if, for example, `expt' returned 0.01,
       ;; `inexact->exact' on that value would yield zero instead of
       ;; 1/100.
       (expt 10 (+ exponent 1 (- digits))))))

(provide/contract [my-round (-> number? (and/c integer? positive? exact?) number?)])