On Thu, Jun 23, 2011 at 2:47 PM, Robert Goldman rpgoldman@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.