bug#51276: Problems with format and scaling floats

Bengt Richter <[email protected]>
Newsgroups gmane.lisp.guile.bugs
Message-ID <20211019084249.GA2281@LionPure>
Nice catch :)

On +2021-10-18 17:22:49 -0400, Timothy Sample wrote:
> Hi Guilers,
> 
> It turns out there’s a little blunder in ‘format’ (from ‘ice-9’).  Look
> at what happens when using the SCALE argument to format a fixed-point
> float (this is Guile from the Git repo at the time of writing):
> 
>     GNU Guile 3.0.7.6-22120
>     Copyright (C) 1995-2021 Free Software Foundation, Inc.
> 
>     Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'.
>     This program is free software, and you are welcome to redistribute it
>     under certain conditions; type `,show c' for details.
> 
>     Enter `,help' for help.
>     scheme@(guile-user)> (format #t "~,,3f~%" 0.00123)
>     0.23
>     $3 = #t
>     scheme@(guile-user)> (format #t "~,,1f~%" 0.00123)
>     ice-9/boot-9.scm:1685:16: In procedure raise-exception:
>     Value out of range 0 to 400: -1
> 
>     Entering a new prompt.  Type `,bt' for a backtrace or `,q' to continue.
> 
> The first example gives the wrong result.  Scaling 0.00123 by 3 should
> yield 1.23, not 0.23.  For the second example, instead of 0.0123, we get
> an error!  What’s going on here?
> 
> Well, our ‘format’ code comes from SLIB and was written in 1998, so it’s
> not easy to explain.  There’s so much mutation even a C programmer would
> blush!  ;)  The issue happens in the ‘format:parse-float’ procedure
> (which is defined inside of ‘format’).  It normalizes the string
> representation of a number, and applies the scale argument when needed.
> It does this by keeping a string of digits and the location of the
> decimal point.  Another thing it keeps track of the leading zeros in a
> variable called ‘left-zeros’.  Here’s the code that does the final
> shifting and places the decimal point:
> 
>     (if (> left-zeros 0)
>         (if (<= left-zeros shift) ; shift always > 0 here
>             (format:fn-shiftleft shift) ; shift out 0s
>             (begin
>               (format:fn-shiftleft left-zeros)
>               (set! format:fn-dot (- shift left-zeros))))
>         (set! format:fn-dot (+ format:fn-dot shift)))
> 
> The issue is that the cases in the inner ‘if’ form are reversed.  That
> is, if there are MORE leading zeros than we need to shift, we can just
> shift.  Otherwise (if there are FEWER leading zeros), we need to shift
> out the zeros and then move the decimal point (‘format:fn-dot’).
> 
> AFAICS, this bug was in the original SLIB implementation (1998) and has
> not been fixed since then.  It’s been in Guile since 1999.
> 
> Anyway, that’s more than anyone cares to know....  Here’s a patch with
> tests!  :)
> 

1999 until now (2021), /and/ reliably reproduced all that time! :)

-- 
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.