Copyright | Bas van Dijk 2013 |
---|---|
License | BSD3 |
Maintainer | Bas van Dijk <v.dijk.bas@gmail.com> |
Safe Haskell | None |
Language | Haskell2010 |
Data.Scientific
provides a space efficient and arbitrary precision
scientific number type.
Scientific
numbers are represented using
scientific notation. It
uses an Integer
coefficient
c
and an Int
base10Exponent
e
(do
note that since we're using an Int
to represent the exponent these numbers
aren't truly arbitrary precision). A scientific number corresponds to the
Fractional
number:
.fromInteger
c * 10 ^^
e
The main application of Scientific
is to be used as the target of parsing
arbitrary precision numbers coming from an untrusted source. The advantages
over using Rational
for this are that:
- A
Scientific
is more efficient to construct. Rational numbers need to be constructed using%
which has to compute thegcd
of thenumerator
anddenominator
. Scientific
is safe against numbers with huge exponents. For example:1e1000000000 ::
will fill up all space and crash your program. Scientific works as expected:Rational
> read "1e1000000000" :: Scientific 1.0e1000000000
- Also, the space usage of converting scientific numbers with huge exponents
to
(like:Integral
sInt
) or
(like:RealFloat
sDouble
orFloat
) will always be bounded by the target type.
WARNING: Although Scientific
is an instance of Fractional
, the methods
are only partially defined! Specifically recip
and /
will diverge
(i.e. loop and consume all space) when their outputs have an infinite decimal
expansion. fromRational
will diverge when the input Rational
has an
infinite decimal expansion.
This module is designed to be imported qualified:
import Data.Scientific as Scientific
- data Scientific
- scientific :: Integer -> Int -> Scientific
- coefficient :: Scientific -> Integer
- base10Exponent :: Scientific -> Int
- isFloating :: Scientific -> Bool
- isInteger :: Scientific -> Bool
- floatingOrInteger :: (RealFloat r, Integral i) => Scientific -> Either r i
- toRealFloat :: RealFloat a => Scientific -> a
- toBoundedRealFloat :: forall a. RealFloat a => Scientific -> Either a a
- toBoundedInteger :: forall i. (Integral i, Bounded i) => Scientific -> Maybe i
- fromFloatDigits :: RealFloat a => a -> Scientific
- formatScientific :: FPFormat -> Maybe Int -> Scientific -> String
- data FPFormat :: *
- toDecimalDigits :: Scientific -> ([Int], Int)
- normalize :: Scientific -> Scientific
Documentation
data Scientific Source
An arbitrary-precision number represented using scientific notation.
This type describes the set of all
which have a finite
decimal expansion.Real
s
A scientific number with coefficient
c
and base10Exponent
e
corresponds to the Fractional
number: fromInteger
c * 10 ^^
e
Eq Scientific | |
Fractional Scientific | WARNING: |
Data Scientific | |
Num Scientific | |
Ord Scientific | |
Read Scientific | |
Real Scientific | WARNING: Avoid applying |
RealFrac Scientific | |
Show Scientific | |
NFData Scientific | |
Hashable Scientific | |
Typeable * Scientific |
Construction
scientific :: Integer -> Int -> Scientific Source
scientific c e
constructs a scientific number which corresponds
to the Fractional
number:
.fromInteger
c * 10 ^^
e
Projections
coefficient :: Scientific -> Integer Source
The coefficient of a scientific number.
Note that this number is not necessarily normalized, i.e. it could contain trailing zeros.
Scientific numbers are automatically normalized when pretty printed or
in toDecimalDigits
.
Use normalize
to do manual normalization.
base10Exponent :: Scientific -> Int Source
The base-10 exponent of a scientific number.
Predicates
isFloating :: Scientific -> Bool Source
Return True
if the scientific is a floating point, False
otherwise.
Also see: floatingOrInteger
.
isInteger :: Scientific -> Bool Source
Return True
if the scientific is an integer, False
otherwise.
Also see: floatingOrInteger
.
Conversions
floatingOrInteger :: (RealFloat r, Integral i) => Scientific -> Either r i Source
floatingOrInteger
determines if the scientific is floating point
or integer. In case it's floating-point the scientific is converted
to the desired RealFloat
using toRealFloat
.
Also see: isFloating
or isInteger
.
toRealFloat :: RealFloat a => Scientific -> a Source
Safely convert a Scientific
number into a RealFloat
(like a Double
or a
Float
).
Note that this function uses realToFrac
(
)
internally but it guards against computing huge Integer magnitudes (fromRational
. toRational
10^e
)
that could fill up all space and crash your program. If the base10Exponent
of the given Scientific
is too big or too small to be represented in the
target type, Infinity or 0 will be returned respectively. Use
toBoundedRealFloat
which explicitly handles this case by returning Left
.
Always prefer toRealFloat
over realToFrac
when converting from scientific
numbers coming from an untrusted source.
toBoundedRealFloat :: forall a. RealFloat a => Scientific -> Either a a Source
Preciser version of toRealFloat
. If the base10Exponent
of the given
Scientific
is too big or too small to be represented in the target type,
Infinity or 0 will be returned as Left
.
toBoundedInteger :: forall i. (Integral i, Bounded i) => Scientific -> Maybe i Source
Convert a Scientific
to a bounded integer.
If the given Scientific
doesn't fit in the target representation, it will
return Nothing
.
This function also guards against computing huge Integer magnitudes (10^e
)
that could fill up all space and crash your program.
fromFloatDigits :: RealFloat a => a -> Scientific Source
Convert a RealFloat
(like a Double
or Float
) into a Scientific
number.
Note that this function uses floatToDigits
to compute the digits
and exponent of the RealFloat
number. Be aware that the algorithm used in
floatToDigits
doesn't work as expected for some numbers, e.g. as
the Double
1e23
is converted to 9.9999999999999991611392e22
, and that
value is shown as 9.999999999999999e22
rather than the shorter 1e23
; the
algorithm doesn't take the rounding direction for values exactly half-way
between two adjacent representable values into account, so if you have a
value with a short decimal representation exactly half-way between two
adjacent representable values, like 5^23*2^e
for e
close to 23, the
algorithm doesn't know in which direction the short decimal representation
would be rounded and computes more digits
Pretty printing
:: FPFormat | |
-> Maybe Int | Number of decimal places to render. |
-> Scientific | |
-> String |
Like show
but provides rendering options.
data FPFormat :: *
Control the rendering of floating point numbers.
toDecimalDigits :: Scientific -> ([Int], Int) Source
Similar to floatToDigits
, toDecimalDigits
takes a
non-negative Scientific
number, and returns a list of digits and
a base-10 exponent. In particular, if x>=0
, and
toDecimalDigits x = ([d1,d2,...,dn], e)
then
n >= 1
x = 0.d1d2...dn * (10^^e)
0 <= di <= 9
null $ takeWhile (==0) $ reverse [d1,d2,...,dn]
The last property means that the coefficient will be normalized, i.e. doesn't contain trailing zeros.
Normalization
normalize :: Scientific -> Scientific Source
Normalize a scientific number by dividing out powers of 10 from the
coefficient
and incrementing the base10Exponent
each time.
You should rarely have a need for this function since scientific numbers are
automatically normalized when pretty-printed and in toDecimalDigits
.