On Sun, Dec 29 2013, Robert Dodier wrote:
> On 2013-12-28, Robert Dodier <robert.dodier at gmail.com> wrote:
>
>> The logic for this stuff is found at lines 315--328 in src/commac.lisp
>> (function EXPLODEN). I looked at it for a few minutes but I don't have
>> an easy fix.
>
> Here is a simple-minded patch. Comments welcome. It just sticks a "0"
> on the result if it starts with ".".
Hi Robert,
The problem with that is that it still won't give the correct result for
0.1860.
I don't think that there is an easy way of tackling this problem with
FORMAT tricks, so I wrote up some simple code this morning for float
formatting, included below (yes, I know, maybe I care about this too
much :P). I believe that when called with EFFECTIVE-PRINTPREC, it would
produce the correct formatting.
If you would be willing to include it in Maxima, I am happy to do some
extensive testing.
--8<---------------cut here---------------start------------->8---
(defun rational-exponent (positive-rational &optional (radix 10))
"Return the largest integer EXPONENT such that
(<= (EXPT RADIX EXPONENT) POSITIVE-RATIONAL)
Calculation is based on logarithms, but is corrected to be exact if necessary."
(check-type radix (integer 2))
(check-type positive-rational (rational (0)))
(let ((exponent (truncate (log positive-rational radix)))) ; may not be exact
(loop
(let ((lower-limit (expt radix exponent)))
(cond ; correction rarely needed
((< positive-rational lower-limit) (decf exponent))
((<= (* radix lower-limit) positive-rational) (incf exponent))
(t (return exponent)))))))
(defun decompose-rational (rational digits &optional (radix 10))
"Decompose a rational into a sign indicator, an exponent (see
RATIONAL-EXPONENT) and a mantissa (a string of digits), returning these as
values. Uses base RADIX."
(check-type radix (integer 2))
(check-type rational rational)
(let* ((abs (abs rational))
(exponent (rational-exponent abs radix))
(mantissa (write-to-string (round (* abs (expt radix (- digits exponent)))
radix)
:base radix :radix nil :escape nil)))
(values (minusp rational) exponent mantissa)))
(defun float-to-string (float significant-digits
&key (radix 10)
(min-exponent -7)
(max-exponent (min (1- significant-digits) 7))
(exponent-marker #\e)
(trailing-decimal-dot nil))
"Return FLOAT printed as STRING with the given number of SIGNIFICANT-DIGITS.
Uses base RADIX, but radix markers are not printed, you have to prepend them.
When (LOG FLOAT RADIX) is between MIN-EXPONENT and MAX-EXPONENT, it is printed
using standard notation, otherwise using scientific notation with the given
EXPONENT-MARKER.
When TRAILING-DECIMAL-DOT, a decimal dot is always printed.
FLOAT is converted to a rational and then rounded to give the required number of
significant digits. This means that some digits may be zeroed out because of
rounding, especially if MAX-EXPONENT is larger than SIGNIFICANT-DIGITS."
(check-type significant-digits (integer 1))
(multiple-value-bind (minusp exponent mantissa)
(decompose-rational (rationalize float) significant-digits radix)
(with-output-to-string (stream)
(labels ((print% (object)
(princ object stream))
(print0 (&optional (n 1))
(loop repeat n do (print% #\0)))
(print-mantissa (dot-position)
(cond
((<= dot-position 0)
(print% "0.")
(print0 (- dot-position))
(princ mantissa stream))
((<= (length mantissa) dot-position)
(princ mantissa stream)
(print0 (- dot-position (length mantissa)))
(when trailing-decimal-dot
(print% #\.)))
(t
(print% (subseq mantissa 0 dot-position))
(print% #\.)
(print% (subseq mantissa dot-position))))))
(when minusp
(print% -))
(cond
((<= min-exponent exponent max-exponent)
(print-mantissa (1+ exponent)))
(t (print-mantissa 1)
(print% exponent-marker)
(print% exponent)))))))
(defun test-float-to-string (float digits &key (min-power -10) (max-power 10))
"Test FLOAT-TO-STRING, returning a list of strings, multiplying FLOAT by powers
of 10."
(loop for power from min-power to max-power
collect (float-to-string (* float (expt 10 power)) digits)))
--8<---------------cut here---------------end--------------->8---
Best,
Tamas