[cl-json-devel] Encoding DOUBLE-FLOAT

Boris Smilga boris.smilga at gmail.com
Wed Jun 22 23:25:18 UTC 2011


On 23 Jun 2011, at 01:10, Robert Goldman wrote:

> On 6/22/11 Jun 22 -11:59 AM, Daniel Brunner wrote:
>> (json:encode-json-to-string 0.0570714084012434D0)
>
> I have replicated this error....
>
> The bug is here:
>
> (defun write-json-number (nr stream)
>   "Write the JSON representation of the number NR to STREAM."
>   (typecase nr
>     (integer (format stream "~d" nr))
>     (real (format stream "~f" nr))
>     (t (unencodable-value-error nr 'write-json-number))))
>
> As can be seen by
>
> CL-USER> (format t "~f" 0.0570714084012434D0)
> 0.0570714084012434D0
> NIL


I have located it, too.  Actually, it's a confusing matter bearing on  
the interpretation of one passage in the Common Lisp standard.  To  
wit, the standard behaviour of the ~F formatting directive (CLHS,  
sec. 22.3.3.1) is to print ‘a sequence of digits, containing a single  
embedded decimal point’.  Exponent markers are never mentioned in  
that section.  However, a little further below it tells us that ‘If  
both w and d [prefix parameters of the directive — B. Sm.] are  
omitted, then the effect is to print the value using ordinary free- 
format output; prin1 uses this format for any number whose magnitude  
is either zero or between 10^-3 (inclusive) and 10^7 (exclusive).’

Now the problem is that the expression ‘ordinary free-format output’  
has no well-defined meaning in the standard.  One reasonable  
interpretation is that it means the same as ‘output produced by print- 
object’.  In this case, the implementer is bound by sec. 22.1.3.1.3:  
‘If the format of the number does not match that specified by *read- 
default-float-format*, then the exponent marker for that format and  
the digit 0 are also printed.’  If we adopt this reading, then CCL  
does the right thing, and SBCL (which prints no exponent marker) is  
in violation of the standard.  However, if we opt for a different  
interpretation (what?), then SBCL might end up compliant after all,  
and CCL might or might not be in violation.

> This suggests that we must avoid using FORMAT in translating floats to
> strings for JSON, or that there's a recipe for getting FORMAT to shun
> the D that I don't know about....

I would advocate a different approach: to check the type of the  
float, and to bind *read-default-float-format* to the corresponding  
format designator:

(defun write-json-number (nr stream)
   "Write the JSON representation of the number NR to STREAM."
   (typecase nr
     (integer (format stream "~d" nr))
     (real (let ((*read-default-float-format*
                  (typecase nr
                    (long-float 'long-float)
                    (double-float 'double-float)
                    (short-float 'short-float)
                    (t 'single-float))))
             (format stream "~f" nr)))
     (t (unencodable-value-error nr 'write-json-number))))

> This checks for D in the output.  I /believe/ that this is the only  
> such
> character that can creep in, but I am far from being an expert on  
> this.

There are also F, L, and S (CLHS, sec. 2.3.2.2).

Yours,
  - B. Sm.





More information about the cl-json-devel mailing list