bug-guile
[Top][All Lists]

## bug#40855: integer-length 0 should be 1

 From: Bengt Richter Subject: bug#40855: integer-length 0 should be 1 Date: Sat, 25 Apr 2020 23:15:40 +0200 User-agent: Mutt/1.10.1 (2018-07-13)

```I'm hoping this is the right place to report this bug :)

To reproduce, guile -c '(format #t "~s\n" (integer-length 0))'
Expected result if correct: 1
Observed result: 0

The following is to support the opinion that 1 is the correct result,
and to explore how integer "length" generalizes to other radixes and
also signed number writing other than sign-magnitude.

Feel free to skip the following if of no interest :)

The tl;dr is:
(integer-length 0) should agree with:
(string-length (number->string 0 2)) =-> 1
-- and not:   (integer-length 0) =-> 0

The integer length in bits (short for binary digits :)
is the number digits required to write the integer value
in conventional big-endian digit order, signifying coefficients
of successive powers of the radix used.

This applies irrespective of the radix. Thus "11" in decimal is
"1*10^1 + 1*10^0"
or hex is
"11*16^0"
or binary is
"1*2^1 + 1*2^0"

So, the crux of the argument is that it takes one digit to write
either 1 or 0:
--8<---------------cut here---------------start------------->8---
Number as radix-2 glyph{0..1} string (unsigned)
"1"
Number as glyphs representing coefficient values of radix polynomial for
number value:
("1")
(1) -- corresponding coefficient values
Number as polynomial expression:
"1*2^0"
(1) -- corresponding term values
1 -- sum of term values
The following should be equal to guile's (integer-length 1):
--8<---------------cut here---------------end--------------->8---

--8<---------------cut here---------------start------------->8---
Number as radix-2 glyph{0..1} string (unsigned)
"0"
Number as glyphs representing coefficient values of radix polynomial for
number value:
("0")
(0) -- corresponding coefficient values
Number as polynomial expression:
"0*2^0"
(0) -- corresponding term values
0 -- sum of term values
The following should be equal to guile's (integer-length 0):
--8<---------------cut here---------------end--------------->8---

BTW, this works for signed numbers as well, if you use a complement
representation making the sign digit 0 for positive and radix-1 for
negative (thus 0 and 1 for radix 2, and e.g. 0 and f for radix 16).

"989" (complement notation)
^--(note that 0 and "9" (radix10 -1) are sign digits for 0 and -1
coefficient values in the polynomial)
Number as glyphs representing coefficient values of radix polynomial for
number value:
("-1" "8" "9")
(-1 8 9) -- corresponding coefficient values
Number as polynomial expression:
"-1*10^2 + 8*10^1 + 9*10^0"
(-100 80 9) -- corresponding term values
-11 -- sum of term values
Tip: for guile integer-length, enter unsigned value with output radix 2

The extreme for this version is radix 36:

"zyz" (complement notation)
^--(note that 0 and "z" (radix36 -1) are sign digits for 0 and -1
coefficient values in the polynomial)
Number as glyphs representing coefficient values of radix polynomial for
number value:
("-1" "y" "z")
(-1 34 35) -- corresponding coefficient values
Number as polynomial expression:
"-1*36^2 + 34*36^1 + 35*36^0"
(-1296 1224 35) -- corresponding term values
-37 -- sum of term values
Tip: for guile integer-length, enter unsigned value with output radix 2

I got a little carried away exploring the complement notation, and wrote
a thing to explain the meanings. Please copy snip to int2poly and chmod 755 it.
Then run once without args for usage help.

I hope it will convince you that guile (integer-length 0) should be 1 ;-)

--8<---------------cut here---------------start------------->8---
#!/usr/bin/env -S guile -e main -s
!#
;; run without arguments for usage, or read below
;; int2poly-0.1.0 -- 2020-04-21

(use-modules (ice-9 format)
(ice-9 regex))

(define self (basename (car (command-line))))
(define verbose #f)

(define (usage)
(begin
(let*((selfxxx (basename (car (command-line)))))
(begin
(format (current-error-port)
"Usage:
where *-radix are entered in decimal, and
but may set independently to 2..36 to demo generality.

-v for verbose output explanations

NUMSTR will be written in radix digits representing
polynomial coefficients, which is presented in series terms
and evaluated back to the original number.

The NUMSTR character set is the same as for (number->string radix)
but could be any chosen set of distinct glyphs for values {0..<radix
-1>}.

guile integer-length can be considered a special case of
coefficient count for radix 2, which is  printed in the last
line of output as \"N integer-digits (radix N)\n"
self ))
(if (= (integer-length 0) 0)
(begin
(format #t "\nGuile bug:
(integer-length 0) should agree with:
(string-length (number->string 0 2)) =-> ~s
-- and not:   (integer-length 0) =-> ~s
And it should agree with integer-digits for ~a 1 and ~a 0
Try ~a 0, and note that it is 1 for any radix 2-36
(string-length (number->string 0 2))
(integer-length 0)
self self self self)
(display " ----\n")
(system "guile -v")
(display "----\n ")
)))))

(define (main argl)
(begin
(set! argl (cdr argl))
(if (not (pair? argl)) (begin (usage) (exit)))
(if (string=? "-v" (car argl)) (begin (set! verbose #t) (set! argl (cdr
argl))))
(if (not (pair? argl)) (begin (usage) (exit)))
(let*((matstr (string-match "[0-9a-z]+" (string-join argl " "))) ;; for
<prefix><substring><suffix>
(sgnstr (match:prefix matstr))    ;; [<sign>]
(absstr (match:substring matstr)) ;;         <inpnum> [<out-radix>]

(matst2 (string-match "[0-9]+" (match:suffix matstr))) ;; demo radix
(radix (if matst2 (string->number (match:substring matst2)) 2)) ;;

(matst3 (if matst2 (string-match "[0-9]+" (match:suffix matst2)) #f))
(iradix (if matst3 (string->number (match:substring matst3)) 10)) ;;

(absnum (string->number absstr iradix)) ;; abs math value of input
(radixp (integer-expt radix (+ 1 abswid))) ;; 2nd bit above msb of
absnum
absnum))) ;; 10abs or 10000-abs

;;          _1xyx

;;      or  10000 if abs=0
(numoff (if (string=? "" sgnstr)
2
(- (string-length numstr1) (+ 1 abswid))))

(numstr (substring numstr1 numoff))
(coeffs (map match:substring  (list-matches "(.)" numstr)))
(coeffs (if (string=? "" sgnstr)
coeffs
(begin
(if (char=? (string-ref numstr 0)
(cons "-1" (cdr coeffs))
coeffs))))
(coeffv (map (lambda (s) (string->number s radix)) coeffs))
(ncoeff (length coeffs))
(terms (string-join (reverse (map (lambda (coeff power)
(begin (format #f "~d*~d^~d" coeff radix
power))) (reverse coeffv) (iota ncoeff))) " + "))
(termv (reverse (map (lambda (coeff power)
(begin (* coeff (integer-expt radix power))))
(reverse coeffv) (iota ncoeff))))
(polyv (apply + termv))

(signword (begin (cond
((string=? "" sgnstr) "unsigned")
((string=? "+" sgnstr) "plus")
((string=? "-" sgnstr) "minus")
(else (throw 'int2poly "bad sign:" sgnstr)))))
(sgnnote (begin (cond
((string=? "" sgnstr) "(unsigned)")
prefix)")
prefix)")
(else (throw 'int2poly "bad sign:" sgnstr)))))
)
(begin
(format #t "Inputs: ~a~a (radix ~s, decimal value ~a ~s) (output radix
(if verbose (format #t "  Number as radix-~s glyph{~a..~a} string ~a\n"
(format #t "  ~s~a\n" numstr (if (string=? "" sgnstr) "" " (complement
notation)"))
(if (and verbose (not (string=? sgnstr "")))
(format #t "   ^--(note that 0 and ~s (radix~a -1) are sign digits
for 0 and -1 coefficient values in the polynomial)\n"
(number->string (-
(format #t "~a  ~s\n" (if verbose "  Number as glyphs representing
coefficient values of radix polynomial for number value:\n" "") coeffs)
(format #t "  ~s~a\n" coeffv (if verbose  " -- corresponding
coefficient values" ""))
(format #t "~a  ~s\n" (if verbose "  Number as polynomial
expression:\n" "") terms)
(format #t "  ~s~a\n" termv (if verbose " -- corresponding term values"
""))
(format #t "  ~s~a\n" polyv (if verbose " -- sum of term values" ""))
(if verbose
(if (and (= radix 2) (string=? sgnstr ""))
(format #t "  The following should be equal to guile's
(integer-length ~s):\n" absnum)
(format #t "  Tip: for guile integer-length, enter unsigned
(format #t "  ~s integer-digit~a (radix ~a)\n" ncoeff (if (> ncoeff 1)
)))
--8<---------------cut here---------------end--------------->8---

--
Regards,
Bengt Richter

```