As far as I can tell, the only way to convert between floating types in Haskell is to use realToFrac. Unfortunately, this function does some insane mangling of values when converting. Some examples:
The last item illustrates an important point: it is impossible to convert a value from Double to CDouble without potentially changing it. This makes it difficult or impossible to use the FFI to call any functions with floating point parameters/return values.
Using a combination of unsafeCoerce (to shoehorn Double values in/out of CDoubles) and some functions written in C (to perform float<=>double), I was able to work around these problems, but that hardly seems like a nice solution.
Edited
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information
-Infinity\r\nrealToFrac (-1/0 :: Float) :: Double\r\n --> -3.402823669209385e38\r\nrealToFrac (-0 :: Double) :: CDouble\r\n --> 0.0\r\n\r\nThe last item illustrates an important point: it is impossible to convert a value from Double to CDouble without potentially changing it. This makes it difficult or impossible to use the FFI to call any functions with floating point parameters/return values.\r\n\r\nUsing a combination of unsafeCoerce (to shoehorn Double values in/out of CDoubles) and some functions written in C (to perform float<=>double), I was able to work around these problems, but that hardly seems like a nice solution.","new":"As far as I can tell, the only way to convert between floating types in Haskell is to use realToFrac. Unfortunately, this function does some insane mangling of values when converting. Some examples:\r\n{{{\r\nrealToFrac (0/0 :: Double) :: Double\r\n --> -Infinity\r\nrealToFrac (-1/0 :: Float) :: Double\r\n --> -3.402823669209385e38\r\nrealToFrac (-0 :: Double) :: CDouble\r\n --> 0.0\r\n}}}\r\nThe last item illustrates an important point: it is impossible to convert a value from Double to CDouble without potentially changing it. This makes it difficult or impossible to use the FFI to call any functions with floating point parameters/return values.\r\n\r\nUsing a combination of unsafeCoerce (to shoehorn Double values in/out of CDoubles) and some functions written in C (to perform float<=>double), I was able to work around these problems, but that hardly seems like a nice solution."},"type_of_failure":{"old":null,"new":null},"blocking":{"old":null,"new":null}} -->
I just discovered that the issue is worse than originally described: the behaviour of realToFrac *changes* if it gets inlined. Consider the following:
module Main where{-# NOINLINE holycow #-}holycow :: (Real a, Fractional b) => a -> bholycow = realToFracmain = do print (realToFrac (0/0 :: Double) :: Double) print (holycow (0/0 :: Double) :: Double)
If you compile without optimisation, both lines print -Infinity as originally described. However, if you enable optimisation, the first line prints NaN (yay!) but the second line still prints -Infinity. GHC seems to be optimising based on the incorrect assumption that realToFrac :: Double -> Double is the identity function.
Yes, that's because the library GHC.Float contains the (clearly incorrect) RULE
"realToFrac/Double->Double" realToFrac = id :: Double -> Double
This ticket really really needs someone who knows and cares about numbers to figure out what the Right Thing is. It's a library issue involving only Haskell source code. Would someone like to help?
my gut feeling has been there should be a function or functions specifically to convert between different floating types. I think this is something that needs to be invented and proposed on libraries@ . (I'm not volunteering to do it this week because I'm going to a conference, but I'm willing to get the conversation started next week)
I think it's pretty straightforward. toRational should be able to convert NaN, Infinity and -Infinity correctly to Rational, and there is already code in fromRational to convert them back to the floating point types correctly (I imagine it's there in order to make things like read "NaN" work).
The Haskell report doesn't say anything about the implementation of toRational, so I think this is correct. I'm validating a patch now.
I've fixed this, but I'm not sure the fix is an improvement.
Thu Feb 11 02:19:55 PST 2010 Simon Marlow <marlowsd@gmail.com> * Handle NaN, -Infinity and Infinity in the toRational for Float/Double (#3676) M ./GHC/Float.lhs -2 +9 M ./GHC/Real.lhs -1 +2
and in ghc:
Thu Feb 11 05:15:43 PST 2010 Simon Marlow <marlowsd@gmail.com> * don't constant fold division that would result in negative zero (#3676)
It has some slightly odd consequences. Someone ought to take a broader look at this in the context of refining the Haskell standard. e.g.
The underlying problem is that the Rational type doesn't really include these strange values; it has been hacked in a few places so that certain unnormalised Rational values are used to represent strange floating point values, e.g. 0 :% 0 represents NaN and fromRational knows about it, but there's no official way to generate one of these (0/0 :: Rational gives an exception), they are only used "internally" by the libraries to make read "NaN" :: Double work.
The change I made is to make toRational (0/0 :: Double) generate 0 :% 0, and similarly for the other strange values, but these had odd consequences, because these special Rational values are not normalised. However, this does fix a real bug (the subject of this ticket).
While it presumably goes against the report, I personally feel that floating point types do not belong as members of the Real class *at all*. The same is true for a number of other classes that they belong to.
In the altfloat package, where I've been experimenting with solutions to this and other problems, I used a class
class FloatConvert a b where toFloating :: a -> b
which provides the necessary conversions: both between floating point types, and from real types to floating point types. It should be noted that the functions double2Float# and float2Double# in GHC.Exts perform correct conversion.