1

An arbitrary number can be readily converted to float with some number of decimals. For example, using (format nil "~,2f" 6) gives "6.00". But is there a way to directly coerce an arbitrary number using an analogous float type spec; i.e., outputting a number, not a string? Alternately, I've tried (read-from-string (format nil "~,2f" 6)), but this does not retain the formatting.

I would like to print a nested tree containing numbers (along with other lisp objects), as floats with two decimal places. For example, a printout might look like X -> (A 3.00 (7.10 B) (C 8.12) 0.75) regardless of the specific number types represented in X. Is set-pprint-dispatch used for this kind of output or is format sufficient?

Jonas Stein
  • 6,826
  • 7
  • 40
  • 72
davypough
  • 1,847
  • 11
  • 21
  • 3
    I think you are confusing a number with its printed representation. – sds Nov 23 '16 at 19:03
  • 1
    Do you mean you want to coerce an integer into a float? As in `(float 6)`=> `6.0`? What do you mean with "retain the formatting"? Numbers in memory don't contain any formatting. You can format any number the way you want when you print them. – jkiiski Nov 23 '16 at 19:06
  • @sds Yes, now I see you are right. What I'm really trying to do is printout a number without the quotes. I should just use (format t "~,2f" 6) instead of (format nil "~,2f" 6). Thanks for clarifying. – davypough Nov 23 '16 at 21:32
  • @davypough, I suggest editing your question to explicitly show that you were expecting `6.00`, but you were getting `"6.00"` - and then, if you feel comfortable that you now understand that `(format t...` was _printing_ `6.00`, while `(format nil ...` was _returning a string_ `"6.00"`, go ahead and write up an answer and accept it for the benefit of future users. – Allison Lock Nov 24 '16 at 07:42

2 Answers2

2

To coerce numbers to float numbers use coerce or float:

CL-USER 121 > (float 2/3 1.0s0)
0.6666667

CL-USER 122 > (float 2/3 1.0d0)
0.6666666666666666D0

CL-USER 123 > (coerce 2/3 'double-float)
0.6666666666666666D0

Note that floats have limited precision - as usual.

Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
2

Is set-pprint-dispatch used for this kind of output or is format sufficient?

Let's try with SET-PPRINT-DISPATCH:

CL-USER> (let ((*print-pprint-dispatch* (copy-pprint-dispatch)))
           (set-pprint-dispatch 'float (lambda (s f) (format s "~,2f" f)))
           (write '(A 3.00 (7.10 B) (C 8.12) 0.75) :pretty t))

(A 3.00 (7.10 B) (C 8.12) 0.75)  ;; standard output
(A 3.0 (7.1 B) (C 8.12) 0.75)    ;; result printed by the REPL

Since the pretty-print dispatch table is copied before being modified, the function associated with floats is only called from inside the let-binding. When the return value of WRITE is printed to the REPL, the default dispatch table is used. You could define a global variable to avoid recomputing the modified table each time you need it.

Note that if you don't give a value for :pretty, then the special *PRINT-PRETTY* variable defines whether the pretty-printer is used or not. For details see 22.2.1.4 Pretty Print Dispatch Tables.

I would like to print a nested tree containing numbers

If what you want is to output any number as a float, just dispatch on the REAL type (complex numbers with non-zero imaginary parts can't be printed as you want, so I don't recommend dispatching on number). Any necessary coercion will happen implicitly:

(let ((*print-pprint-dispatch* (copy-pprint-dispatch)))
  (set-pprint-dispatch 'real (lambda (s f) (format s "~,2f" f)))
  (write '(A 3 (7.1d0 B) (C 203/25) #C(3/4 0)) :pretty t))

... writes:

(A 3.00 (7.10 B) (C 8.12) 0.75)
coredump
  • 37,664
  • 5
  • 43
  • 77
  • 1
    The application for this is a summary printout, so it works great. I've added `in-package` statements before & after `write` to suppress printing the package prefix for non-reals; and embedded the `write` statement in a loop to print each item in the top-level of the input tree on a separate line. (Please comment further if these are moot changes. Thank you for helping to organize my thinking about this.) – davypough Nov 25 '16 at 19:19
  • You're welcome, thank you. You can locally bind `*package*` instead of using `in-package`, which is simpler because you don't have to revert to the previous package explicitly, which could be error-prone. But I'd rather set `:escape` to NIL in the call to write to avoid printing packages. See http://clhs.lisp.se/Body/v_pr_esc.htm#STprint-escapeST. You could also define custom pretty printer functions for symbols, like for numbers. – coredump Nov 26 '16 at 18:57