[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