summaryrefslogtreecommitdiff
path: root/libraries/base/Numeric.hs
diff options
context:
space:
mode:
authorsimonmar <unknown>2001-07-04 12:07:27 +0000
committersimonmar <unknown>2001-07-04 12:07:27 +0000
commitd69a1776505122b7dc5977d1daa4afc102582eaf (patch)
tree9a009402a9c3af27e42140b1738ddea74e0e50a8 /libraries/base/Numeric.hs
parenteb7eff67e729994865cd9dc8acd3c7d850ec6103 (diff)
downloadhaskell-d69a1776505122b7dc5977d1daa4afc102582eaf.tar.gz
[project @ 2001-07-04 12:07:27 by simonmar]
Add Numeric library here for the time being. This is a combination of the H98 Numeric library and a few functions from GHC's NumExts.
Diffstat (limited to 'libraries/base/Numeric.hs')
-rw-r--r--libraries/base/Numeric.hs367
1 files changed, 367 insertions, 0 deletions
diff --git a/libraries/base/Numeric.hs b/libraries/base/Numeric.hs
new file mode 100644
index 0000000000..4a4ecf4577
--- /dev/null
+++ b/libraries/base/Numeric.hs
@@ -0,0 +1,367 @@
+-----------------------------------------------------------------------------
+--
+-- Module : Numeric
+-- Copyright : (c) The University of Glasgow 2001
+-- License : BSD-style (see the file libraries/core/LICENSE)
+--
+-- Maintainer : libraries@haskell.org
+-- Stability : experimental
+-- Portability : portable
+--
+-- $Id: Numeric.hs,v 1.1 2001/07/04 12:07:27 simonmar Exp $
+--
+-- Odds and ends, mostly functions for reading and showing
+-- RealFloat-like kind of values.
+--
+-----------------------------------------------------------------------------
+
+module Numeric (
+
+ fromRat, -- :: (RealFloat a) => Rational -> a
+ showSigned, -- :: (Real a) => (a -> ShowS) -> Int -> a -> ShowS
+ readSigned, -- :: (Real a) => ReadS a -> ReadS a
+ showInt, -- :: Integral a => a -> ShowS
+ readInt, -- :: (Integral a) => a -> (Char -> Bool)
+ -- -> (Char -> Int) -> ReadS a
+
+ readDec, -- :: (Integral a) => ReadS a
+ readOct, -- :: (Integral a) => ReadS a
+ readHex, -- :: (Integral a) => ReadS a
+
+ showHex, -- :: Integral a => a -> ShowS
+ showOct, -- :: Integral a => a -> ShowS
+ showBin, -- :: Integral a => a -> ShowS
+
+ showEFloat, -- :: (RealFloat a) => Maybe Int -> a -> ShowS
+ showFFloat, -- :: (RealFloat a) => Maybe Int -> a -> ShowS
+ showGFloat, -- :: (RealFloat a) => Maybe Int -> a -> ShowS
+ showFloat, -- :: (RealFloat a) => a -> ShowS
+ readFloat, -- :: (RealFloat a) => ReadS a
+
+
+ floatToDigits, -- :: (RealFloat a) => Integer -> a -> ([Int], Int)
+ lexDigits, -- :: ReadS String
+
+ -- general purpose number->string converter.
+ showIntAtBase, -- :: Integral a
+ -- => a -- base
+ -- -> (a -> Char) -- digit to char
+ -- -> a -- number to show.
+ -- -> ShowS
+ ) where
+
+import Prelude -- For dependencies
+import Data.Char
+
+#ifdef __GLASGOW_HASKELL__
+import GHC.Base ( Char(..), unsafeChr )
+import GHC.Read
+import GHC.Real ( showSigned )
+import GHC.Float
+#endif
+
+#ifdef __HUGS__
+import Array
+#endif
+
+#ifdef __GLASGOW_HASKELL__
+showInt :: Integral a => a -> ShowS
+showInt n cs
+ | n < 0 = error "Numeric.showInt: can't show negative numbers"
+ | otherwise = go n cs
+ where
+ go n cs
+ | n < 10 = case unsafeChr (ord '0' + fromIntegral n) of
+ c@(C# _) -> c:cs
+ | otherwise = case unsafeChr (ord '0' + fromIntegral r) of
+ c@(C# _) -> go q (c:cs)
+ where
+ (q,r) = n `quotRem` 10
+
+-- Controlling the format and precision of floats. The code that
+-- implements the formatting itself is in @PrelNum@ to avoid
+-- mutual module deps.
+
+{-# SPECIALIZE showEFloat ::
+ Maybe Int -> Float -> ShowS,
+ Maybe Int -> Double -> ShowS #-}
+{-# SPECIALIZE showFFloat ::
+ Maybe Int -> Float -> ShowS,
+ Maybe Int -> Double -> ShowS #-}
+{-# SPECIALIZE showGFloat ::
+ Maybe Int -> Float -> ShowS,
+ Maybe Int -> Double -> ShowS #-}
+
+showEFloat :: (RealFloat a) => Maybe Int -> a -> ShowS
+showFFloat :: (RealFloat a) => Maybe Int -> a -> ShowS
+showGFloat :: (RealFloat a) => Maybe Int -> a -> ShowS
+
+showEFloat d x = showString (formatRealFloat FFExponent d x)
+showFFloat d x = showString (formatRealFloat FFFixed d x)
+showGFloat d x = showString (formatRealFloat FFGeneric d x)
+#endif
+
+#ifdef __HUGS__
+-- This converts a rational to a floating. This should be used in the
+-- Fractional instances of Float and Double.
+
+fromRat :: (RealFloat a) => Rational -> a
+fromRat x =
+ if x == 0 then encodeFloat 0 0 -- Handle exceptional cases
+ else if x < 0 then - fromRat' (-x) -- first.
+ else fromRat' x
+
+-- Conversion process:
+-- Scale the rational number by the RealFloat base until
+-- it lies in the range of the mantissa (as used by decodeFloat/encodeFloat).
+-- Then round the rational to an Integer and encode it with the exponent
+-- that we got from the scaling.
+-- To speed up the scaling process we compute the log2 of the number to get
+-- a first guess of the exponent.
+fromRat' :: (RealFloat a) => Rational -> a
+fromRat' x = r
+ where b = floatRadix r
+ p = floatDigits r
+ (minExp0, _) = floatRange r
+ minExp = minExp0 - p -- the real minimum exponent
+ xMin = toRational (expt b (p-1))
+ xMax = toRational (expt b p)
+ p0 = (integerLogBase b (numerator x) -
+ integerLogBase b (denominator x) - p) `max` minExp
+ f = if p0 < 0 then 1 % expt b (-p0) else expt b p0 % 1
+ (x', p') = scaleRat (toRational b) minExp xMin xMax p0 (x / f)
+ r = encodeFloat (round x') p'
+
+-- Scale x until xMin <= x < xMax, or p (the exponent) <= minExp.
+scaleRat :: Rational -> Int -> Rational -> Rational ->
+ Int -> Rational -> (Rational, Int)
+scaleRat b minExp xMin xMax p x =
+ if p <= minExp then
+ (x, p)
+ else if x >= xMax then
+ scaleRat b minExp xMin xMax (p+1) (x/b)
+ else if x < xMin then
+ scaleRat b minExp xMin xMax (p-1) (x*b)
+ else
+ (x, p)
+
+-- Exponentiation with a cache for the most common numbers.
+minExpt = 0::Int
+maxExpt = 1100::Int
+expt :: Integer -> Int -> Integer
+expt base n =
+ if base == 2 && n >= minExpt && n <= maxExpt then
+ expts!n
+ else
+ base^n
+
+expts :: Array Int Integer
+expts = array (minExpt,maxExpt) [(n,2^n) | n <- [minExpt .. maxExpt]]
+
+-- Compute the (floor of the) log of i in base b.
+-- Simplest way would be just divide i by b until it's smaller then b,
+-- but that would be very slow! We are just slightly more clever.
+integerLogBase :: Integer -> Integer -> Int
+integerLogBase b i =
+ if i < b then
+ 0
+ else
+ -- Try squaring the base first to cut down the number of divisions.
+ let l = 2 * integerLogBase (b*b) i
+ doDiv :: Integer -> Int -> Int
+ doDiv i l = if i < b then l else doDiv (i `div` b) (l+1)
+ in doDiv (i `div` (b^l)) l
+
+
+-- Misc utilities to show integers and floats
+
+showEFloat :: (RealFloat a) => Maybe Int -> a -> ShowS
+showFFloat :: (RealFloat a) => Maybe Int -> a -> ShowS
+showGFloat :: (RealFloat a) => Maybe Int -> a -> ShowS
+showFloat :: (RealFloat a) => a -> ShowS
+
+showEFloat d x = showString (formatRealFloat FFExponent d x)
+showFFloat d x = showString (formatRealFloat FFFixed d x)
+showGFloat d x = showString (formatRealFloat FFGeneric d x)
+showFloat = showGFloat Nothing
+
+-- These are the format types. This type is not exported.
+
+data FFFormat = FFExponent | FFFixed | FFGeneric
+
+formatRealFloat :: (RealFloat a) => FFFormat -> Maybe Int -> a -> String
+formatRealFloat fmt decs x = s
+ where base = 10
+ s = if isNaN x then
+ "NaN"
+ else if isInfinite x then
+ if x < 0 then "-Infinity" else "Infinity"
+ else if x < 0 || isNegativeZero x then
+ '-' : doFmt fmt (floatToDigits (toInteger base) (-x))
+ else
+ doFmt fmt (floatToDigits (toInteger base) x)
+ doFmt fmt (is, e) =
+ let ds = map intToDigit is
+ in case fmt of
+ FFGeneric ->
+ doFmt (if e < 0 || e > 7 then FFExponent else FFFixed)
+ (is, e)
+ FFExponent ->
+ case decs of
+ Nothing ->
+ case ds of
+ ['0'] -> "0.0e0"
+ [d] -> d : ".0e" ++ show (e-1)
+ d:ds -> d : '.' : ds ++ 'e':show (e-1)
+ Just dec ->
+ let dec' = max dec 1 in
+ case is of
+ [0] -> '0':'.':take dec' (repeat '0') ++ "e0"
+ _ ->
+ let (ei, is') = roundTo base (dec'+1) is
+ d:ds = map intToDigit
+ (if ei > 0 then init is' else is')
+ in d:'.':ds ++ "e" ++ show (e-1+ei)
+ FFFixed ->
+ case decs of
+ Nothing ->
+ let f 0 s ds = mk0 s ++ "." ++ mk0 ds
+ f n s "" = f (n-1) (s++"0") ""
+ f n s (d:ds) = f (n-1) (s++[d]) ds
+ mk0 "" = "0"
+ mk0 s = s
+ in f e "" ds
+ Just dec ->
+ let dec' = max dec 0 in
+ if e >= 0 then
+ let (ei, is') = roundTo base (dec' + e) is
+ (ls, rs) = splitAt (e+ei) (map intToDigit is')
+ in (if null ls then "0" else ls) ++
+ (if null rs then "" else '.' : rs)
+ else
+ let (ei, is') = roundTo base dec'
+ (replicate (-e) 0 ++ is)
+ d : ds = map intToDigit
+ (if ei > 0 then is' else 0:is')
+ in d : '.' : ds
+
+roundTo :: Int -> Int -> [Int] -> (Int, [Int])
+roundTo base d is = case f d is of
+ (0, is) -> (0, is)
+ (1, is) -> (1, 1 : is)
+ where b2 = base `div` 2
+ f n [] = (0, replicate n 0)
+ f 0 (i:_) = (if i >= b2 then 1 else 0, [])
+ f d (i:is) =
+ let (c, ds) = f (d-1) is
+ i' = c + i
+ in if i' == base then (1, 0:ds) else (0, i':ds)
+
+--
+-- Based on "Printing Floating-Point Numbers Quickly and Accurately"
+-- by R.G. Burger and R. K. Dybvig, in PLDI 96.
+-- This version uses a much slower logarithm estimator. It should be improved.
+
+-- This function returns a list of digits (Ints in [0..base-1]) and an
+-- exponent.
+
+floatToDigits :: (RealFloat a) => Integer -> a -> ([Int], Int)
+
+floatToDigits _ 0 = ([0], 0)
+floatToDigits base x =
+ let (f0, e0) = decodeFloat x
+ (minExp0, _) = floatRange x
+ p = floatDigits x
+ b = floatRadix x
+ minExp = minExp0 - p -- the real minimum exponent
+ -- Haskell requires that f be adjusted so denormalized numbers
+ -- will have an impossibly low exponent. Adjust for this.
+ (f, e) = let n = minExp - e0
+ in if n > 0 then (f0 `div` (b^n), e0+n) else (f0, e0)
+
+ (r, s, mUp, mDn) =
+ if e >= 0 then
+ let be = b^e in
+ if f == b^(p-1) then
+ (f*be*b*2, 2*b, be*b, b)
+ else
+ (f*be*2, 2, be, be)
+ else
+ if e > minExp && f == b^(p-1) then
+ (f*b*2, b^(-e+1)*2, b, 1)
+ else
+ (f*2, b^(-e)*2, 1, 1)
+ k =
+ let k0 =
+ if b==2 && base==10 then
+ -- logBase 10 2 is slightly bigger than 3/10 so
+ -- the following will err on the low side. Ignoring
+ -- the fraction will make it err even more.
+ -- Haskell promises that p-1 <= logBase b f < p.
+ (p - 1 + e0) * 3 `div` 10
+ else
+ ceiling ((log (fromInteger (f+1)) +
+ fromIntegral e * log (fromInteger b)) /
+ log (fromInteger base))
+ fixup n =
+ if n >= 0 then
+ if r + mUp <= expt base n * s then n else fixup (n+1)
+ else
+ if expt base (-n) * (r + mUp) <= s then n
+ else fixup (n+1)
+ in fixup k0
+
+ gen ds rn sN mUpN mDnN =
+ let (dn, rn') = (rn * base) `divMod` sN
+ mUpN' = mUpN * base
+ mDnN' = mDnN * base
+ in case (rn' < mDnN', rn' + mUpN' > sN) of
+ (True, False) -> dn : ds
+ (False, True) -> dn+1 : ds
+ (True, True) -> if rn' * 2 < sN then dn : ds else dn+1 : ds
+ (False, False) -> gen (dn:ds) rn' sN mUpN' mDnN'
+ rds =
+ if k >= 0 then
+ gen [] r (s * expt base k) mUp mDn
+ else
+ let bk = expt base (-k)
+ in gen [] (r * bk) s (mUp * bk) (mDn * bk)
+ in (map fromIntegral (reverse rds), k)
+#endif
+
+-- ---------------------------------------------------------------------------
+-- Integer printing functions
+
+showIntAtBase :: Integral a => a -> (a -> Char) -> a -> ShowS
+showIntAtBase base toChr n r
+ | n < 0 = error ("Numeric.showIntAtBase: applied to negative number " ++ show n)
+ | otherwise =
+ case quotRem n base of { (n', d) ->
+ let c = toChr d in
+ seq c $ -- stricter than necessary
+ let
+ r' = c : r
+ in
+ if n' == 0 then r' else showIntAtBase base toChr n' r'
+ }
+
+showHex :: Integral a => a -> ShowS
+showHex n r =
+ showString "0x" $
+ showIntAtBase 16 (toChrHex) n r
+ where
+ toChrHex d
+ | d < 10 = chr (ord '0' + fromIntegral d)
+ | otherwise = chr (ord 'a' + fromIntegral (d - 10))
+
+showOct :: Integral a => a -> ShowS
+showOct n r =
+ showString "0o" $
+ showIntAtBase 8 (toChrOct) n r
+ where toChrOct d = chr (ord '0' + fromIntegral d)
+
+showBin :: Integral a => a -> ShowS
+showBin n r =
+ showString "0b" $
+ showIntAtBase 2 (toChrOct) n r
+ where toChrOct d = chr (ord '0' + fromIntegral d)