double2Float is faster than (fromRational . toRational)

Dear Haskellers, I just want to share an observation. I had to convert a Double to a Float value in an inner loop of an application, and I used somethin like this: xf = (fromRational $ toRational xd) :: Float The program works on windows but it did not on OSX - it was too slow. Now, after big headaches and much frustration, I replaced the code above with this line (why didn't I come up with this earlier?): xf = double2Float xd and now everything works just fine. I am not really surprised by the speed-up (and no-one should be), but I am still surprised how often such kinds of unobvious problems occur while programming in Haskell. So I write this email just to remind me and you to look out for such pitfalls. Best, Daniel

On Fri, 21 May 2010, Daniel van den Eijkel wrote:
Dear Haskellers,
I just want to share an observation. I had to convert a Double to a Float value in an inner loop of an application, and I used somethin like this:
xf = (fromRational $ toRational xd) :: Float
I think realToFrac is the function to use here, and this might be replaced by double2Float by an optimizer rule. I think double2Float is from a GHC package and thus one should avoid to call double2Float directly.

I see. And I changed the code, it works well. Thanks for that! Daniel Henning Thielemann schrieb:
On Fri, 21 May 2010, Daniel van den Eijkel wrote:
Dear Haskellers,
I just want to share an observation. I had to convert a Double to a Float value in an inner loop of an application, and I used somethin like this:
xf = (fromRational $ toRational xd) :: Float
I think realToFrac is the function to use here, and this might be replaced by double2Float by an optimizer rule. I think double2Float is from a GHC package and thus one should avoid to call double2Float directly.

On Friday 21 May 2010 22:06:43, Henning Thielemann wrote:
On Fri, 21 May 2010, Daniel van den Eijkel wrote:
Dear Haskellers,
I just want to share an observation. I had to convert a Double to a Float value in an inner loop of an application, and I used somethin like this:
xf = (fromRational $ toRational xd) :: Float
I think realToFrac is the function to use here, and this might be replaced by double2Float by an optimizer rule. I think double2Float is from a GHC package and thus one should avoid to call double2Float directly.
In GHC.Real: -- | general coercion to fractional types realToFrac :: (Real a, Fractional b) => a -> b realToFrac = fromRational . toRational {-# RULES "realToFrac/Int->Int" realToFrac = id :: Int -> Int #-} There are more rules elsewhere. If you compile with optimisations, GHC turns your realToFrac into double2Float# nicely, so it's okay to use realToFrac. However, without optimisations, no rules fire, so you'll get (fromRational . toRational).

Daniel Fischer wrote:
There are more rules elsewhere. If you compile with optimisations, GHC turns your realToFrac into double2Float# nicely, so it's okay to use realToFrac. However, without optimisations, no rules fire, so you'll get (fromRational . toRational).
That must be new, because it didn't used to be the case. Also, rewrite rules can be fragile. Not to mention that the (fromRational . toRational) definition is incorrect for converting between Float and Double because Rational cannot properly encode the transfinite values in Float/Double. The robust solution is to use the RealToFrac class from the logfloat package: http://hackage.haskell.org/packages/archive/logfloat/0.12.1/doc/html/Data-Nu... -- Live well, ~wren

On Sunday 23 May 2010 13:12:16, wren ng thornton wrote:
Daniel Fischer wrote:
There are more rules elsewhere. If you compile with optimisations, GHC turns your realToFrac into double2Float# nicely, so it's okay to use realToFrac. However, without optimisations, no rules fire, so you'll get (fromRational . toRational).
That must be new, because it didn't used to be the case.
GHC.Float.lhs contains the rules for (realToFrac :: (Float|Double) -> (Float|Double)) since 6.8.1 at least, I didn't look at previous releases.
Also, rewrite rules can be fragile.
True. I don't see how you would construct such a situation for these rules, but if several rules match a piece of code, you can't know which one fires first, perhaps making the others not match anymore. And inlining may prevent rules from matching, too. If you need rewrite rules, it is advisable to check whether they actually fired.
Not to mention that the (fromRational . toRational) definition is incorrect for converting between Float and Double because Rational cannot properly encode the transfinite values in Float/Double.
The robust solution is to use the RealToFrac class from the logfloat package:
http://hackage.haskell.org/packages/archive/logfloat/0.12.1/doc/html/Dat a-Number-RealToFrac.html
Yes.

dvde:
Dear Haskellers,
I just want to share an observation. I had to convert a Double to a Float value in an inner loop of an application, and I used somethin like this:
xf = (fromRational $ toRational xd) :: Float
The program works on windows but it did not on OSX - it was too slow. Now, after big headaches and much frustration, I replaced the code above with this line (why didn't I come up with this earlier?):
xf = double2Float xd
and now everything works just fine.
I am not really surprised by the speed-up (and no-one should be), but I am still surprised how often such kinds of unobvious problems occur while programming in Haskell. So I write this email just to remind me and you to look out for such pitfalls.
There's no rewrite rule for this optimization. There's a ticket though with some of the solutions. -- Don

By the way, speaking of floating-point precision, is there a real reason why haskell forces us to write : foreign import ccall unsafe "math.h frexp" c_frexp::CDouble->(Ptr CInt)->IO () foreign import ccall unsafe "math.h ldexp" c_ldexp::CDouble->CInt->IO CDouble ulp::Double->Double ulp x=unsafePerformIO $ do expon<-alloca (\e->do c_frexp (realToFrac x) e peek e) (c_ldexp 0.5 $ expon-52) >>= return.realToFrac To allow us to change the IEEE-754 rounding mode ? Shouldn't it be rather "roundDown $ x+3*y/z" or "roundUp $ (cos x)**y" ? Moreover, due to laziness it appears quite difficult (at least to me !) to implement these roundDown and roundUp functions with C calls to select a hardware rounding mode, then unsafePerformIO. It would be way faster than an allocation (on the stack, I agree), then C calls with a memory access (probably cached, but...) in between ! Does anyone know how this translates to LLVM (maybe in the forthcoming GHC backend) ? If anyone has got an answer or a solution... Cheers, PE El 21/05/2010, a las 17:10, Don Stewart escribió:
dvde:
Dear Haskellers,
I just want to share an observation. I had to convert a Double to a Float value in an inner loop of an application, and I used somethin like this:
xf = (fromRational $ toRational xd) :: Float
The program works on windows but it did not on OSX - it was too slow. Now, after big headaches and much frustration, I replaced the code above with this line (why didn't I come up with this earlier?):
xf = double2Float xd
and now everything works just fine.
I am not really surprised by the speed-up (and no-one should be), but I am still surprised how often such kinds of unobvious problems occur while programming in Haskell. So I write this email just to remind me and you to look out for such pitfalls.
There's no rewrite rule for this optimization. There's a ticket though with some of the solutions.
-- Don _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

Hello Daniel, Friday, May 21, 2010, 11:55:35 PM, you wrote:
xf = (fromRational $ toRational xd) :: Float xf = double2Float xd
am still surprised how often such kinds of unobvious problems occur while programming in Haskell
does it mean that all other languages you are used doesn't have such problem or that you are an inexperienced programmer? :) -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com
participants (7)
-
Bulat Ziganshin
-
Daniel Fischer
-
Daniel van den Eijkel
-
Don Stewart
-
Henning Thielemann
-
Pierre-Etienne Meunier
-
wren ng thornton