numio.scm 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. ; Copyright (c) 1993-2008 by Richard Kelsey and Jonathan Rees. See file COPYING.
  2. ;;;; number->string and string->number
  3. ; NUMBER->STRING
  4. (define-generic real-number->string &number->string)
  5. (define (number->string number . maybe-radix)
  6. (let ((radix (if (null? maybe-radix)
  7. 10
  8. (car maybe-radix))))
  9. (if (and (number? number)
  10. (or (null? maybe-radix)
  11. (and (null? (cdr maybe-radix))
  12. (integer? radix)
  13. (exact? radix)
  14. (< 0 radix))))
  15. (real-number->string number radix)
  16. (apply call-error
  17. "invalid argument"
  18. 'number->string
  19. number
  20. maybe-radix))))
  21. (define-method &number->string (n radix)
  22. (call-error "invalid argument"
  23. 'number->string
  24. n
  25. radix))
  26. (define-method &number->string ((n :exact-integer) radix)
  27. (integer->string n radix))
  28. (define integer->string ;Won't necessarily work if n is inexact
  29. (let ()
  30. (define (integer->string n radix)
  31. (let ((magnitude
  32. (if (= n 0)
  33. (list #\0)
  34. (let recur ((n n) (l '()))
  35. (if (= n 0)
  36. l
  37. (recur (quotient n radix)
  38. (cons (integer->digit (abs (remainder n radix)))
  39. l)))))))
  40. (list->string (if (>= n 0)
  41. magnitude
  42. (cons #\- magnitude)))))
  43. (define (integer->digit n)
  44. (ascii->char (+ n (if (< n 10)
  45. zero
  46. a-minus-10))))
  47. (define zero (char->ascii #\0))
  48. (define a-minus-10 (- (char->ascii #\a) 10))
  49. integer->string))
  50. ; STRING->NUMBER
  51. ; This just strips off any # prefixes and hands the rest off to
  52. ; really-string->number, which is generic.
  53. (define (string->number string . options)
  54. (if (not (string? string))
  55. (apply call-error "invalid argument"
  56. 'string->number
  57. string options))
  58. (let* ((radix (cond ((null? options) 10)
  59. ((null? (cdr options)) (car options))
  60. ;; Revised^3 Scheme compatibility
  61. (else (cadr options))))
  62. (radix (case radix
  63. ((2 8 10 16) radix)
  64. ((b) 2) ((o) 8) ((d) 10) ((x) 16) ;R3RS only?
  65. (else (call-error "invalid radix"
  66. 'string->number
  67. string radix))))
  68. (len (string-length string)))
  69. (let loop ((pos 0) (exactness? #f) (exact? #t) (radix? #f) (radix radix))
  70. (cond ((>= pos len)
  71. #f)
  72. ((char=? (string-ref string pos) #\#)
  73. (let ((pos (+ pos 1)))
  74. (if (>= pos len)
  75. #f
  76. (let ((radix-is
  77. (lambda (radix)
  78. (if radix?
  79. #f
  80. (loop (+ pos 1) exactness? exact? #t radix))))
  81. (exactness-is
  82. (lambda (exact?)
  83. (if exactness?
  84. #f
  85. (loop (+ pos 1) #t exact? radix? radix)))))
  86. (case (char-downcase (string-ref string pos))
  87. ((#\b) (radix-is 2))
  88. ((#\o) (radix-is 8))
  89. ((#\d) (radix-is 10))
  90. ((#\x) (radix-is 16))
  91. ((#\e) (exactness-is #t))
  92. ((#\i) (exactness-is #f))
  93. (else #f))))))
  94. (else
  95. (really-string->number
  96. (substring string pos len)
  97. radix
  98. (if exactness?
  99. exact?
  100. (let loop ((pos pos))
  101. (cond ((>= pos len) #t) ;exact
  102. ((char=? (string-ref string pos) #\.)
  103. (if (not (= radix 10))
  104. (warn "non-base-10 number has decimal point"
  105. string))
  106. #f) ;inexact
  107. ((char=? (string-ref string pos) #\#)
  108. #f)
  109. ((and (= radix 10)
  110. (case (char-downcase (string-ref string pos))
  111. ;; One day, we have to include #\s #\f #\d #\l.
  112. ;; We don't now because STRING->FLOAT actually does the
  113. ;; wrong thing for these currently, so we'd rather barf.
  114. ((#\e) #t)
  115. (else #f)))
  116. #f)
  117. (else (loop (+ pos 1))))))))))))
  118. (define-generic really-string->number &really-string->number)
  119. (define-method &really-string->number (string radix xact?) #f)
  120. ; Read exact integers
  121. (define-simple-type :integer-string (:string)
  122. (lambda (s)
  123. (and (string? s)
  124. (let loop ((i (- (string-length s) 1)))
  125. (if (< i 0)
  126. #t
  127. (let ((c (string-ref s i)))
  128. (and (or (char-numeric? c)
  129. (and (char>=? c #\a)
  130. (char<=? c #\f))
  131. (and (char>=? c #\A)
  132. (char<=? c #\F))
  133. (and (= i 0)
  134. (or (char=? c #\+) (or (char=? c #\-)))))
  135. (loop (- i 1)))))))))
  136. (define-method &really-string->number ((string :integer-string) radix xact?)
  137. (let ((n (string->integer string radix)))
  138. (if n
  139. (set-exactness n xact?)
  140. (next-method)))) ; we might have something like 1e10
  141. (define (set-exactness n xact?)
  142. (if (exact? n)
  143. (if xact? n (exact->inexact n))
  144. (if xact? (inexact->exact n) n)))
  145. (define string->integer
  146. (let ()
  147. (define (string->integer string radix)
  148. (cond ((= (string-length string) 0) #f)
  149. ((char=? (string-ref string 0) #\+)
  150. (do-it string 1 1 radix))
  151. ((char=? (string-ref string 0) #\-)
  152. (do-it string 1 -1 radix))
  153. (else
  154. (do-it string 0 1 radix))))
  155. (define (do-it string pos sign radix)
  156. (let* ((len (string-length string)))
  157. (if (>= pos len)
  158. #f
  159. (let loop ((n 0) (pos pos))
  160. (if (>= pos len)
  161. n
  162. (let ((d (digit->integer (string-ref string pos)
  163. radix)))
  164. (if d
  165. (loop (+ (* n radix) (* sign d))
  166. (+ pos 1))
  167. #f)))))))
  168. (define (digit->integer c radix)
  169. (cond ((char-numeric? c)
  170. (let ((n (- (char->ascii c) zero)))
  171. (if (< n radix) n #f)))
  172. ((<= radix 10) #f)
  173. (else
  174. (let ((n (- (char->ascii (char-downcase c)) a-minus-ten)))
  175. (if (and (>= n 10) (< n radix)) n #f)))))
  176. (define zero (char->ascii #\0))
  177. (define a-minus-ten (- (char->ascii #\a) 10))
  178. string->integer))