home *** CD-ROM | disk | FTP | other *** search
/ PSION CD 2 / PsionCDVol2.iso / Programs / 876 / hugs.sis / Numeric.hs < prev    next >
Encoding:
Text File  |  2000-09-21  |  9.6 KB  |  248 lines

  1. -----------------------------------------------------------------------------
  2. -- Standard Library: Numeric operations
  3. --
  4. -- Suitable for use with Hugs 98
  5. -----------------------------------------------------------------------------
  6.  
  7. module Numeric(fromRat,
  8.                showSigned, showInt,
  9.                readSigned, readInt,
  10.                readDec, readOct, readHex, 
  11.                floatToDigits,
  12.                showEFloat, showFFloat, showGFloat, showFloat, 
  13.                readFloat, lexDigits) where
  14.  
  15. -- Many of these functions have been moved to the Prelude.
  16. -- The RealFloat instances in the Prelude do not use this floating
  17. -- point format routine.
  18.  
  19. import Char
  20. import Array
  21.  
  22. -- This converts a rational to a floating.  This should be used in the
  23. -- Fractional instances of Float and Double.
  24.  
  25. fromRat :: (RealFloat a) => Rational -> a
  26. fromRat x = 
  27.     if x == 0 then encodeFloat 0 0              -- Handle exceptional cases
  28.     else if x < 0 then - fromRat' (-x)          -- first.
  29.     else fromRat' x
  30.  
  31. -- Conversion process:
  32. -- Scale the rational number by the RealFloat base until
  33. -- it lies in the range of the mantissa (as used by decodeFloat/encodeFloat).
  34. -- Then round the rational to an Integer and encode it with the exponent
  35. -- that we got from the scaling.
  36. -- To speed up the scaling process we compute the log2 of the number to get
  37. -- a first guess of the exponent.
  38. fromRat' :: (RealFloat a) => Rational -> a
  39. fromRat' x = r
  40.   where b = floatRadix r
  41.         p = floatDigits r
  42.         (minExp0, _) = floatRange r
  43.         minExp = minExp0 - p            -- the real minimum exponent
  44.         xMin = toRational (expt b (p-1))
  45.         xMax = toRational (expt b p)
  46.         p0 = (integerLogBase b (numerator x) -
  47.               integerLogBase b (denominator x) - p) `max` minExp
  48.         f = if p0 < 0 then 1 % expt b (-p0) else expt b p0 % 1
  49.         (x', p') = scaleRat (toRational b) minExp xMin xMax p0 (x / f)
  50.         r = encodeFloat (round x') p'
  51.  
  52. -- Scale x until xMin <= x < xMax, or p (the exponent) <= minExp.
  53. scaleRat :: Rational -> Int -> Rational -> Rational -> 
  54.              Int -> Rational -> (Rational, Int)
  55. scaleRat b minExp xMin xMax p x =
  56.     if p <= minExp then
  57.         (x, p)
  58.     else if x >= xMax then
  59.         scaleRat b minExp xMin xMax (p+1) (x/b)
  60.     else if x < xMin  then
  61.         scaleRat b minExp xMin xMax (p-1) (x*b)
  62.     else
  63.         (x, p)
  64.  
  65. -- Exponentiation with a cache for the most common numbers.
  66. minExpt = 0::Int
  67. maxExpt = 1100::Int
  68. expt :: Integer -> Int -> Integer
  69. expt base n =
  70.     if base == 2 && n >= minExpt && n <= maxExpt then
  71.         expts!n
  72.     else
  73.         base^n
  74.  
  75. expts :: Array Int Integer
  76. expts = array (minExpt,maxExpt) [(n,2^n) | n <- [minExpt .. maxExpt]]
  77.  
  78. -- Compute the (floor of the) log of i in base b.
  79. -- Simplest way would be just divide i by b until it's smaller then b,
  80. -- but that would be very slow!  We are just slightly more clever.
  81. integerLogBase :: Integer -> Integer -> Int
  82. integerLogBase b i =
  83.      if i < b then
  84.         0
  85.      else
  86.         -- Try squaring the base first to cut down the number of divisions.
  87.         let l = 2 * integerLogBase (b*b) i
  88.             doDiv :: Integer -> Int -> Int
  89.             doDiv i l = if i < b then l else doDiv (i `div` b) (l+1)
  90.         in  doDiv (i `div` (b^l)) l
  91.  
  92.  
  93. -- Misc utilities to show integers and floats 
  94.  
  95. showEFloat     :: (RealFloat a) => Maybe Int -> a -> ShowS
  96. showFFloat     :: (RealFloat a) => Maybe Int -> a -> ShowS
  97. showGFloat     :: (RealFloat a) => Maybe Int -> a -> ShowS
  98. showFloat      :: (RealFloat a) => a -> ShowS
  99.  
  100. showEFloat d x =  showString (formatRealFloat FFExponent d x)
  101. showFFloat d x =  showString (formatRealFloat FFFixed d x)
  102. showGFloat d x =  showString (formatRealFloat FFGeneric d x)
  103. showFloat      =  showGFloat Nothing 
  104.  
  105. -- These are the format types.  This type is not exported.
  106.  
  107. data FFFormat = FFExponent | FFFixed | FFGeneric
  108.  
  109. formatRealFloat :: (RealFloat a) => FFFormat -> Maybe Int -> a -> String
  110. formatRealFloat fmt decs x = s
  111.   where base = 10
  112.         s = if isNaN x then 
  113.                 "NaN"
  114.             else if isInfinite x then 
  115.                 if x < 0 then "-Infinity" else "Infinity"
  116.             else if x < 0 || isNegativeZero x then 
  117.                 '-' : doFmt fmt (floatToDigits (toInteger base) (-x))
  118.             else 
  119.                 doFmt fmt (floatToDigits (toInteger base) x)
  120.         doFmt fmt (is, e) =
  121.             let ds = map intToDigit is
  122.             in  case fmt of
  123.                 FFGeneric -> 
  124.                     doFmt (if e < 0 || e > 7 then FFExponent else FFFixed)
  125.                           (is, e)
  126.                 FFExponent ->
  127.                     case decs of
  128.                     Nothing ->
  129.                         case ds of
  130.                          ['0'] -> "0.0e0"
  131.                          [d]   -> d : ".0e" ++ show (e-1)
  132.                          d:ds  -> d : '.' : ds ++ 'e':show (e-1)
  133.                     Just dec ->
  134.                         let dec' = max dec 1 in
  135.                         case is of
  136.                          [0] -> '0':'.':take dec' (repeat '0') ++ "e0"
  137.                          _ ->
  138.                           let (ei, is') = roundTo base (dec'+1) is
  139.                               d:ds = map intToDigit
  140.                                          (if ei > 0 then init is' else is')
  141.                           in d:'.':ds  ++ "e" ++ show (e-1+ei)
  142.                 FFFixed ->
  143.                     case decs of
  144.                     Nothing ->
  145.                         let f 0 s ds = mk0 s ++ "." ++ mk0 ds
  146.                             f n s "" = f (n-1) (s++"0") ""
  147.                             f n s (d:ds) = f (n-1) (s++[d]) ds
  148.                             mk0 "" = "0"
  149.                             mk0 s = s
  150.                         in  f e "" ds
  151.                     Just dec ->
  152.                         let dec' = max dec 0 in
  153.                         if e >= 0 then
  154.                             let (ei, is') = roundTo base (dec' + e) is
  155.                                 (ls, rs) = splitAt (e+ei) (map intToDigit is')
  156.                             in  (if null ls then "0" else ls) ++ 
  157.                                 (if null rs then "" else '.' : rs)
  158.                         else
  159.                             let (ei, is') = roundTo base dec'
  160.                                               (replicate (-e) 0 ++ is)
  161.                                 d : ds = map intToDigit
  162.                                             (if ei > 0 then is' else 0:is')
  163.                             in  d : '.' : ds
  164.  
  165. roundTo :: Int -> Int -> [Int] -> (Int, [Int])
  166. roundTo base d is = case f d is of
  167.                 (0, is) -> (0, is)
  168.                 (1, is) -> (1, 1 : is)
  169.   where b2 = base `div` 2
  170.         f n [] = (0, replicate n 0)
  171.         f 0 (i:_) = (if i >= b2 then 1 else 0, [])
  172.         f d (i:is) = 
  173.             let (c, ds) = f (d-1) is
  174.                 i' = c + i
  175.             in  if i' == base then (1, 0:ds) else (0, i':ds)
  176.  
  177. --
  178. -- Based on "Printing Floating-Point Numbers Quickly and Accurately"
  179. -- by R.G. Burger and R. K. Dybvig, in PLDI 96.
  180. -- This version uses a much slower logarithm estimator.  It should be improved.
  181.  
  182. -- This function returns a list of digits (Ints in [0..base-1]) and an
  183. -- exponent.
  184.  
  185. floatToDigits :: (RealFloat a) => Integer -> a -> ([Int], Int)
  186.  
  187. floatToDigits _ 0 = ([0], 0)
  188. floatToDigits base x =
  189.     let (f0, e0) = decodeFloat x
  190.         (minExp0, _) = floatRange x
  191.         p = floatDigits x
  192.         b = floatRadix x
  193.         minExp = minExp0 - p            -- the real minimum exponent
  194.         -- Haskell requires that f be adjusted so denormalized numbers
  195.         -- will have an impossibly low exponent.  Adjust for this.
  196.         (f, e) = let n = minExp - e0
  197.                  in  if n > 0 then (f0 `div` (b^n), e0+n) else (f0, e0)
  198.  
  199.         (r, s, mUp, mDn) =
  200.            if e >= 0 then
  201.                let be = b^e in
  202.                if f == b^(p-1) then
  203.                    (f*be*b*2, 2*b, be*b, b)
  204.                else
  205.                    (f*be*2, 2, be, be)
  206.            else
  207.                if e > minExp && f == b^(p-1) then
  208.                    (f*b*2, b^(-e+1)*2, b, 1)
  209.                else
  210.                    (f*2, b^(-e)*2, 1, 1)
  211.         k = 
  212.             let k0 =
  213.                     if b==2 && base==10 then
  214.                         -- logBase 10 2 is slightly bigger than 3/10 so
  215.                         -- the following will err on the low side.  Ignoring
  216.                         -- the fraction will make it err even more.
  217.                         -- Haskell promises that p-1 <= logBase b f < p.
  218.                         (p - 1 + e0) * 3 `div` 10
  219.                     else
  220.                         ceiling ((log (fromInteger (f+1)) + 
  221.                                  fromInt e * log (fromInteger b)) / 
  222.                                   log (fromInteger base))
  223.                 fixup n =
  224.                     if n >= 0 then
  225.                         if r + mUp <= expt base n * s then n else fixup (n+1)
  226.                     else
  227.                         if expt base (-n) * (r + mUp) <= s then n
  228.                                                            else fixup (n+1)
  229.             in  fixup k0
  230.  
  231.         gen ds rn sN mUpN mDnN =
  232.             let (dn, rn') = (rn * base) `divMod` sN
  233.                 mUpN' = mUpN * base
  234.                 mDnN' = mDnN * base
  235.             in  case (rn' < mDnN', rn' + mUpN' > sN) of
  236.                 (True,  False) -> dn : ds
  237.                 (False, True)  -> dn+1 : ds
  238.                 (True,  True)  -> if rn' * 2 < sN then dn : ds else dn+1 : ds
  239.                 (False, False) -> gen (dn:ds) rn' sN mUpN' mDnN'
  240.         rds =
  241.             if k >= 0 then
  242.                 gen [] r (s * expt base k) mUp mDn
  243.             else
  244.                 let bk = expt base (-k)
  245.                 in  gen [] (r * bk) s (mUp * bk) (mDn * bk)
  246.     in  (map toInt (reverse rds), k)
  247.  
  248.