Data.Scientific

scientific-0.3.3.8: Numbers represented using scientific notation

Copyright | Bas van Dijk 2013
License | BSD3
Maintainer | Bas van Dijk <v.dijk.bas@gmail.com>
Safe Haskell | None
Language | Haskell2010

Data.Scientific

Contents

- Construction
- Projections
- Predicates
- Conversions
- Pretty printing
- Normalization

Description

`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 the `gcd` of the `numerator` and `denominator`.
- `Scientific` is safe against numbers with huge exponents. For example: `1e1000000000 :: `Rational`` will fill up all space and crash your program. Scientific works as expected:

    > read "1e1000000000" :: Scientific
    1.0e1000000000

- Also, the space usage of converting scientific numbers with huge exponents to ``Integral`s` (like: `Int`) or ``RealFloat`s` (like: `Double` or `Float`) 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

Synopsis

- 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 :: *
  - = Exponent
  - | Fixed
  - | Generic
- toDecimalDigits :: Scientific -> ([Int], Int)
- normalize :: Scientific -> Scientific

# Documentation

data Scientific

An arbitrary-precision number represented using scientific notation.

This type describes the set of all ``Real`s` which have a finite decimal expansion.

A scientific number with `coefficient` `c` and `base10Exponent` `e` corresponds to the `Fractional` number: ``fromInteger` c * 10 `^^` e`

Instances

Eq Scientific |
Fractional Scientific | WARNING: `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.
Data Scientific |
Num Scientific |
Ord Scientific |
Read Scientific |
Real Scientific | WARNING: `toRational` needs to compute the `Integer` magnitude: `10^e`. If applied to a huge exponent this could fill up all space and crash your program! Avoid applying `toRational` (or `realToFrac`) to scientific numbers coming from an untrusted source and use `toRealFloat` instead. The latter guards against excessive space usage.
RealFrac Scientific |
Show Scientific |
NFData Scientific |
Hashable Scientific |

# Construction

scientific :: Integer -> Int -> Scientific

`scientific c e` constructs a scientific number which corresponds to the `Fractional` number: ``fromInteger` c * 10 `^^` e`.

# Projections

coefficient :: Scientific -> Integer

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

The base-10 exponent of a scientific number.

# Predicates

isFloating :: Scientific -> Bool

Return `True` if the scientific is a floating point, `False` otherwise.

Also see: `floatingOrInteger`.

isInteger :: Scientific -> Bool

Return `True` if the scientific is an integer, `False` otherwise.

Also see: `floatingOrInteger`.

# Conversions

floatingOrInteger :: (RealFloat r, Integral i) => Scientific -> Either r i

`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

Safely convert a `Scientific` number into a `RealFloat` (like a `Double` or a `Float`).

Note that this function uses `realToFrac` (``fromRational` . `toRational``) internally but it guards against computing huge Integer magnitudes (`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

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

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

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

formatScientific

Arguments

:: 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.

Constructors

Exponent | Scientific notation (e.g. `2.3e123`).
Fixed | Standard decimal notation.
Generic | Use decimal notation for values between `0.1` and `9,999,999`, and scientific notation otherwise.

Instances

Enum FPFormat |
Read FPFormat |
Show FPFormat |

toDecimalDigits :: Scientific -> ([Int], Int)

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

1.     n >= 1

2.     x = 0.d1d2...dn * (10^^e)

3.     0 <= di <= 9

4.     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

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`.