Skip to content

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
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information