bug#42012: Reference Manual and Docstring on number->string

Bengt Richter <[email protected]>
Newsgroups gmane.lisp.guile.bugs
Message-ID <20200624034958.GA2949@LionPure>
On +2020-06-23 10:34:12 +0200, [email protected] wrote:
> On Tue, Jun 23, 2020 at 12:51:23AM +0200, Sebastian Miele wrote:
> > Guile 3.0.3. The reference manual and the docstring of number->string
> > say: "If N is inexact, a radix of 10 will be used." But that is not what
> > happens, e.g.,
> > 
> >   (let ((x 4.0)) (and (inexact? x) (number->string x 3)))
> > 
> > evaluates to "11.0" instead of #f or "4.0". Probably "if RADIX is not
> > supplied, a radix of 10 will be used" is meant.
> 
> Confirmed for 3.0.2. At first I thought that the fractional part being
> zero could be significant (as in your example), but
> 
>   scheme@(guile-user)> (number->string 0.3333333333333333 3)
>   $5 = "0.1"
> 
> is clearly being done in radix 3, fractional part and all.
> 
> Cheers
> -- t

There are some other things that might be mentioned also,
like the characters used by number->string and more surprisingly by string->number :-)

You can try copying the following into ./show-string2num-chars.scm somewhere and chmod 755 it
and try running it, but it will produce one item per line, so it will be handy to let pr format that, like
 ./show-string2num-chars.scm|pr -t -8|uniq|sed -e 's:^:;;;; :'
 Unless your system is different, you should get the table in the comments below.

Since string->number will accept characters past #\z there may be a bug?
See some experiments and speculations below the "BTW" ;-)

--8<---------------cut here---------------start------------->8---
#!/usr/bin/env -S guile --no-auto-compile -e main -s
!#
;;;; show-string2num-chars.scm
(define (main args)
  (begin
    (let*((chrs255 (cdr (map integer->char (iota 256))))
	  (ints255 (map (lambda (c) (string->number (string c) 256)) chrs255))
	  (c2iprs  (map (lambda (c i) (cons c i)) chrs255 ints255))
	  (okprs (filter (lambda (pr) (cdr pr)) c2iprs))
	  ;;
	  )
      (begin
	(map (lambda (pr) (format #t "~a=~a\n" (car pr) (cdr pr))) okprs)
	;;
	))
    ;;
    ))

;;;; running this from emacs right after the following line showing the emacs command:
;;;; Esc 1 Esc ! ./show-string2num-chars.scm|pr -t -8|uniq|sed -e 's:^:;;;; :'
;;;; 0=0	 9=9	  I=18	   R=27	    Z=35     h=17     p=25     x=33
;;;; 1=1	 A=10	  J=19	   S=28	    a=10     i=18     q=26     y=34
;;;; 2=2	 B=11	  K=20	   T=29	    b=11     j=19     r=27     z=35
;;;; 3=3	 C=12	  L=21	   U=30	    c=12     k=20     s=28     {=36
;;;; 4=4	 D=13	  M=22	   V=31	    d=13     l=21     t=29     |=37
;;;; 5=5	 E=14	  N=23	   W=32	    e=14     m=22     u=30     }=38
;;;; 6=6	 F=15	  O=24	   X=33	    f=15     n=23     v=31     ~=39
;;;; 7=7	 G=16	  P=25	   Y=34	    g=16     o=24     w=32     =40
;;;; 8=8	 H=17	  Q=26

;;;; #\z=35 #\{=36 #\|=37 #\}=38 #\delete=#f 
;;;; scheme@(guile-user) [6]> 

;;;; BTW

;;;; scheme@(guile-user) [6]> (string-for-each (lambda (c) (format #t "~s=~a " c (string->number (string c) 40))) "z{|}\x7f")(newline)
;;;;
;;;; #\z=35 #\{=36 #\|=37 #\}=38 #\delete=#f
;;;; scheme@(guile-user) [6]>  

;;;; it will also apparently happily accept multiple characters z through \x7f and convert them with any radix exceeding
;;;; the largest "digit value
;;;; scheme@(guile-user) [8]> (for-each (lambda (s) (format #t "~s=~a " s (string->number s 1000))) '("0" "1" "10" "z{" "|}" "\x7f")) (newline)
;;;; "0"=0 "1"=1 "10"=1000 "z{"=35036 "|}"=37038 "\x7f"=40 
;;;; scheme@(guile-user) [8]> 

;;;; the following makes me think possibly the unicode value is taken and masked with 0x7f for ascii and then
;;;; accepted if >= 97 (#\a) (accidentally including > 122 (#\z), only checking for [0-9] and [A-Z] if maked value is less than 97 (#\a)
;;;;scheme@(guile-user) [8]> (for-each (lambda (s) (format #t "~s=~a " s (string->number s 1000))) '("0" "1" "10" "111" "z{" "|}" "\x7f\x80" "\x80\x7f" "\u807e")) (newline)
;;;; "0"=0 "1"=1 "10"=1000 "111"=1001001 "z{"=35036 "|}"=37038 "\x7f\x80"=40041 "\x80\x7f"=#f "聾"=39 

;;;; I'll leave it to others to explore further :)

;;;; Note, though, that the inverse -- number->string -- will not accept a radix outide of 2-36:
;;;; scheme@(guile-user) [10]> (number->string (+ (* 34 36) 35) 36)
;;;; $18 = "yz"
;;;; scheme@(guile-user) [10]> (number->string (+ (* 34 36) 35) 40)
;;;; ERROR: In procedure number->string:
;;;; Value out of range 2 to 36: 40
;;;; 
;;;; Entering a new prompt.  Type `,bt' for a backtrace or `,q' to continue.
;;;; scheme@(guile-user) [11]> (number->string (+ (* 34 36) 35) 37)
;;;; ERROR: In procedure number->string:
;;;; Value out of range 2 to 36: 37
;;;; 
--8<---------------cut here---------------end--------------->8---

-- 
Regards,
Bengt Richter
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.