From: drector@math.uci.edu (David Rector)
Newsgroups: comp.lang.smalltalk
Subject: Printing Floats in Smalltalk/V (corrected)
Date: 22 Jul 92 21:16:52 GMT

In May I posted a rewrite of the Smalltalk/V printOn routine for Floats.
There exists a bug in my rewrite in a little used branch dealing with
rounding mantissas to 1.0.  A number of the form

      1.0eN * (1.0 - epsilon)

with N between 0 and 7 and epsilon less than roundoff will print incorrectly.
Below is a corrected version of my rewrite.  I am indebted to Mark Johnson,
Cal Poly, San Luis Obispo, for pointing this bug out to me.


--------------cut here---------------------cut here---------------------
"printnum.st - Corrections to printOn for floats in Smalltalk/V"
               (corrected)"!

" This file reimplements Number method raisedToInteger: and
Float method printOn:.  In addition, it adds the capability of 
printing as many digits of a floating point number as desired 
with a new method printOn:digits: and the capability to change
the default number of digits printed using printOn: by a new
class method digits:.
    The reimplementation of raisedToInteger: replaces a
O(exponent) algorithm by an  O(log2(exponent)) algorithm.
A similar saving in speed is achieved in printOn:, both
as a consequence of the reimplementation of raisedToIntger,
and by using the intrinsic ln routine for the initial estimate of
the base 10 exponent.  The overall saving in execution time of 
printOn: is over 2 to 1 for small exponents and 8 to 1 for large.
(386 Windows version with coprocessor).
    The new printOn: and printOn:digits: methods emulate the behavior
--for good or ill--of the DOS distribution printOn:.  Numbers in the 
range (+/-) 1.0 to 99999999.9 are printed without exponent and with up 
to 8 digits to the left of the decimal point.  Other numbers are printed
in standard floating point notation with 8 (or the number specified)
significant digits; trailing zeros are surpressed.
    WARNING: These methods have not yet been extensively tested with
various versions of Smalltalk/V.  I can make no warranty of their
correctness or suitability.  In particular, the 1991 Mac version
of Smalltalk/V has shown a distressing sensitivity to recompilation
of class Float.  In one accidental experiment in changing printOn:
(the previous release version of printOn: was used), the compiler 
crashed and damaged the V.sources file.  The methods given here 
have compiled successfuly, but I have not--for any version--tested 
unintended side effects of changing the default number of digits 
for printing floats.
    Copyright notice: This code is copyright 1992 by D. L. Rector.
Permission is granted for its free distribution and use as long
as this notice is left intact, and no attempt is made to restrict use
of this code by others (see Free Software Foundation Copyleft notice).
    Please report any difficulties or suggestions, preferably by 
E-mail, to
        David L. Rector
        Dept. of Math.
        U.C. Irvine,
        Irvine, CA 92717
        drector@math.uci.edu
"!

Number variableByteSubclass: #Float
  classVariableNames:
    'Digits '
  poolDictionaries: '' !

! Float class methods !

digits: anInteger
        "Set the default number of significant digits to use
         in printing floating point numbers.  Answers old value"
        |old|
    old := Digits.
    Digits := (2 max: anInteger) min: 20.
    ^old !

!

"For IBM, uncomment the next line"!

Float digits: 8 !

"For 1991 Mac version, uncomment the next line"!

"Float digits: 11" !


"WARNING: the printOn: method must follow the printOn:digits: method
 in initial compilation.  Otherwise, printing of floating point numbers
 may be disabled and the compiler may crash."!

! Float methods !

printOn: aStream digits: anInteger
        "Append the ASCII representation
         of the receiver to aStream
         rounded to anInteger significant digits"
        |exponent sign x mantissa length value m n|
    n := 2 max: anInteger.
    sign := self negative.
    x := self abs.
    x = 0.0 ifTrue: [^aStream nextPutAll: '0.0'].
    exponent := (x ln / 10.0 ln) truncated.
    x := exponent negative
             ifTrue:  [ x  *  (10.0 raisedToInteger: exponent negated)]
             ifFalse: [x / (10.0 raisedToInteger: exponent)].
    x >= 10.0
        ifTrue:
            [exponent := exponent + 1.
             x := x / 10.0]
        ifFalse:
            [x < 1.0
                ifTrue:
                    [exponent := exponent - 1.
                     x := x * 10.0]].
    (exponent between: 0 and: 7)
        ifTrue: [m := exponent + 1.
                 n := n max: (m + 1)]
        ifFalse: [m := 1].
    x := x + (0.5 / (10.0 raisedToInteger: n - 1)).
    x >= 10.0
        ifTrue:
            [exponent := exponent + 1.
             x := 1.0.                           " Bug fix "
             (exponent between: 0 and: 7)        "         "
                ifTrue: [m := exponent + 1.      "         "
                         n := n max: (m + 1)]    "         "
                ifFalse: [m := 1]].              " 7/22/92 "
    mantissa := String new: n.
    length := m + 1.
    1 to: n do:
        [:i | value := x truncated.
            x := 10.0 * (x - value asFloat).
            value = 0
                ifTrue:
                    [mantissa at: i put: $0]
                ifFalse:
                    [mantissa at: i put:
                        (Character value: value + 48).
                     length := length max: i]].
    sign ifTrue: [aStream nextPut: $-].
    1 to: m do:
        [:i | aStream nextPut: (mantissa at: i)].
    aStream nextPut: $..
    m + 1 to: length do:
        [:i | aStream nextPut: (mantissa at: i)].
    (m = 1 and: [exponent ~= 0])
        ifTrue:
            [aStream nextPut: $e.
             exponent printOn: aStream] !

printOn: aStream
        "Append the ASCII representation
         of the receiver to aStream"
    ^self printOn: aStream digits: Digits !

!

! Number methods !

raisedToInteger: anInteger
        "Answer the receiver raised
         to the power of anInteger."
    | answer exponent power|
    (anInteger isKindOf: Integer)
        ifTrue: [
            answer := 1.
            power := self.
            exponent := anInteger abs.
            [exponent > 1]
                whileTrue:
                    [(exponent bitAnd: 1) = 0
                        ifFalse: [answer := answer * power].
                     exponent := exponent bitShift: -1.
                     power := power * power].
            exponent = 0
                ifFalse: [answer := answer * power].
            anInteger < 0
                ifTrue:  [^1 / answer]
                ifFalse: [^answer]].
    self error: 'raisedToInteger needs integer power' !

!
----------------------cut here---------------------cut here-------------
--
David L. Rector				drector@math.uci.edu
Dept. of Math.				U. C. Irvine, Irvine CA 92717
Newsgroups: comp.lang.smalltalk