numio.scm 5.5 KB

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