Generic number type
From HaskellWiki
m |
(→See also: link to "converting numbers") |
||
| Line 117: | Line 117: | ||
== See also == | == See also == | ||
| + | * [[Converting numbers]] | ||
* The discussion on haskell-cafe which provided the impetus for this page: http://www.haskell.org/pipermail/haskell-cafe/2007-June/027092.html | * The discussion on haskell-cafe which provided the impetus for this page: http://www.haskell.org/pipermail/haskell-cafe/2007-June/027092.html | ||
Current revision
Contents |
1 Problem
Question:
Can I have a generic numeric data type in Haskell which coversAnswer: In principle you can define a type like
data GenericNumber = Integer Integer | Rational Rational | Double Double
However you will find that it is difficult to implement these methods in a way that is appropriate for each use case. There is simply no type that can emulate the others.
Floating point numbers are imprecise -that all scripting language users have encountered so far (or ignored :-).
A2 Idiomatic solutions
It is strongly advised to carefully check whether aSo let's revisit some examples and their idiomatic solutions in plain Haskell 98.
2.1 average
You may find it cumbersome to manually convert integers to fractional number types like in
average :: Fractional a => [a] -> a average xs = sum xs / fromIntegral (length xs)
and you may prefer
average :: [GenericNumber] -> GenericNumber average xs = sum xs / genericNumberLength xs
average :: Fractional a => [a] -> a average xs = sum xs / genericLength xs
2.2 ratios
You find it easy to write
1 / 3 :: Rational
but uncomfortable that
1 / floor pi :: Rational
does not work.
The first example works, because the numeric literals1 % 3 :: Rational 1 % floor pi :: Rational
2.3 isSquare
It may seem irksome thatisSquare :: (Integral a) => a -> Bool isSquare n = (round . sqrt $ fromIntegral n) ^ 2 == n
isSquare :: GenericNumber -> Bool isSquare n = (round . sqrt $ n) ^ 2 == n
2.4 squareRoot
Closely related is the (floor of the) square root of integers. It is tempting to implement
squareRoot :: Integer -> Integer squareRoot = floor . sqrt . (fromIntegral :: Integer -> Double)
This will not work for several reasons:
- For a square number, may give a result slightly below an integer, whichsqrtwill round down to the next integer.floor
- will not preserve the (arbitrary high) precision offromIntegrals and thus will not give precise results.Integer
- may exceed the maximum exponent of the floating point representation and fail with an overflow error orfromIntegralresult.Infinity
The most efficient way is to call the native implementation of the square root of GNU's multiprecision library. (How to do that?) The most portable way is to implement a square root algorithm from scratch.
(^!) :: Num a => a -> Int -> a (^!) x n = x^n squareRoot :: Integer -> Integer squareRoot 0 = 0 squareRoot 1 = 1 squareRoot n = let twopows = iterate (^!2) 2 (lowerRoot, lowerN) = last $ takeWhile ((n>=) . snd) $ zip (1:twopows) twopows newtonStep x = div (x + div n x) 2 iters = iterate newtonStep (squareRoot (div n lowerN) * lowerRoot) isRoot r = r^!2 <= n && n < (r+1)^!2 in head $ dropWhile (not . isRoot) iters
3 See also
- Converting numbers
- The discussion on haskell-cafe which provided the impetus for this page: http://www.haskell.org/pipermail/haskell-cafe/2007-June/027092.html
Categories: FAQ | Mathematics | Idioms
