floor, ceiling, round :: Double -> Int are awesomely slow
We have super-naive implementations of the RealFrac class for Double.
Consider:
{-# RULES "truncate/Double->Int" truncate = double2Int #-}
instance RealFrac Double where
properFraction x
= case (decodeFloat x) of { (m,n) ->
let b = floatRadix x in
if n >= 0 then
(fromInteger m * fromInteger b ^ n, 0.0)
else
case (quotRem m (b^(negate n))) of { (w,r) ->
(fromInteger w, encodeFloat r n)
}
}
floor x = case properFraction x of
(n,r) -> if r < 0.0 then n - 1 else n
So now, we *do* have a good rule for truncate, but floor, ceiling and round turn out to be awesomely slow.
main = print . sumU
. mapU (floor :: Double -> Int)
$ enumFromToFracU 0 100000000
Runs in 1 minute, 10 seconds:
$ time ./henning
5000000050000000
./henning 70.25s user 0.17s system 99% cpu 1:10.99 total
Now, if we just replace that with a ccall to math.h:floor, we get:
main = print . sumU
. mapU (floor' :: Double -> Int)
$ enumFromToFracU 0 100000000
floor' :: Double -> Int
floor' x = (truncate :: Double -> Int) (c_floor x)
{-# INLINE floor' #-}
foreign import ccall unsafe "math.h floor"
c_floor :: Double -> Double
Which runs in 1.8 seconds:
$ time ./henning
5000000050000000
./henning 1.88s user 0.00s system 99% cpu 1.884 total
Similar results for ceiling and round (see the main ticket for RealFrac, http://hackage.haskell.org/trac/ghc/ticket/1434)
Action
Use math.h versions of round, floor and ceiling for Double and Float?
Trac metadata
| Trac field | Value |
|---|---|
| Version | 6.8.2 |
| Type | Bug |
| TypeOfFailure | OtherFailure |
| Priority | normal |
| Resolution | Unresolved |
| Component | Compiler |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | |
| Operating system | Unknown |
| Architecture | Unknown |