[cl-json-devel] Encoding DOUBLE-FLOAT

Boris Smilga boris.smilga at gmail.com
Thu Jun 23 22:15:16 UTC 2011


On Thu, Jun 23, 2011 at 2:47 PM, Robert Goldman <rpgoldman at sift.info> wrote:
> On 6/23/11 Jun 23 -7:05 AM, Boris Smilga wrote:
>> What about rationals?  They too are a subtype of REAL, but your
>> etypecase doesn't take them into account.
>
> Should we check for rationals before we check for reals (i.e., check for
> integer, rational, real, and then have the error case)?  And presumably
> we should print rationals as if they are floats, correct?

Certainly.  Moreover, per CLHS sec. 22.3.3.1, to format a rational
with ~F, it is coerced to be a single float.  If
*read-default-float-format* is bound to something else, we'll get an S
marker:

(let ((*read-default-float-format* 'double-float))
  (format nil "~F" 2/3))
⇒ "0.6666667S0"                  ; Actually so in CCL.

As we have no way to predict what  *read-default-float-format* will be
when the user calls encode-json, the invocation of format in the
rational clause of typecase has to explicitly bind
*read-default-float-format* to 'single-float:

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

Since rational is a subtype of real, we can factor the clause down
into etypecase, to make the code more concise:

(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*
                (etypecase nr
                  (short-float 'short-float)
                  ((single-float rational) 'single-float)
                  (double-float 'double-float)
                  (long-float 'long-float))))
           (format stream "~f" nr)))
   (t (unencodable-value-error nr 'write-json-number))))

This is largely the same thing which I had proposed three messages up
the thread.  The order of clauses doesn't matter much, because, per
the definition of the system class float in CLHS sec. 12.2, ‘any two
of [the types short-float, single-float, double-float, and long-float]
must be either disjoint types or the same type’.

> I think in principle we should try to detect the relationship between
> the different float subtypes.  Presumably we can do this using
> MOST-POSITIVE-SHORT-FLOAT, MOST-POSITIVE-SINGLE-FLOAT, etc.., etc.,
> etc., and that would be portable...  If I get a chance, I will try to do
> that: looks like it will be a little cumbersome.

Hmmm... I'm afraid I don't really get your idea.  The relationship
between different float types is laid down quite explicitly in the
Standard (should it be called ‘the Most Holy and Exalted Standard’?).
Checking the limits won't give us anything, as the format of the
number is orthogonal to its magnitude: 3.0D0 is a double, not a single
float (in implementations which have distinct double and single
floats), even though its numeric value falls inside the interval
between least-positive-single-float and most-positive-single-float,
and can be represented by a single float (without loss of precision).
Pray, what exactly are you trying to accomplish here?

Yours,
 - B. Sm.




More information about the cl-json-devel mailing list