How do i define a Binary instance for Data.Number.BigFloat?
I've defined an instance for LongDouble by writing:
instance Binary LongDouble where
put d = put (decodeFloat d)
get = do
x <- get
y <- get
return $! encodeFloat x y
However trying the following doesn't work:
instance Binary (BigFloat e) where
put d = put (decodeFloat d )
get = do
x <- get
y <- get
return $! encodeFloat x y
GHC gives this error message:
/home/usr/Documents/src/Lib.hs:27:18: error:
• No instance for (Epsilon e) arising from a use of ‘decodeFloat’
Possible fix:
add (Epsilon e) to the context of the instance declaration
• In the first argument of ‘put’, namely ‘(decodeFloat d)’
In the expression: put (decodeFloat d)
In an equation for ‘put’: put d = put (decodeFloat d)
|
27 | put d = put (decodeFloat d )
| ^^^^^^^^^^^^^
/home/usr/Documents/src/Lib.hs:31:19: error:
• No instance for (Epsilon e) arising from a use of ‘encodeFloat’
Possible fix:
add (Epsilon e) to the context of the instance declaration
• In the second argument of ‘($!)’, namely ‘encodeFloat x y’
In a stmt of a 'do' block: return $! encodeFloat x y
In the expression:
do x <- get
y <- get
return $! encodeFloat x y
|
31 | return $! encodeFloat x y
If I provide a specific type for e, such as Prec500, i get this message :
• Illegal instance declaration for ‘Binary (BigFloat Prec500)’
(All instance types must be of the form (T a1 ... an)
where a1 ... an are *distinct type variables*,
and each type variable appears at most once in the instance head.
Use FlexibleInstances if you want to disable this.)
• In the instance declaration for ‘Binary (BigFloat Prec500)’
And using FlexibleInstances compiles, but doesn't result in correctly encoded numbers.
The following also compiles but doesn't encode correctly either:
instance Epsilon e => Binary (BigFloat e) where
put d = put (decodeFloat d )
get = do
x <- get
y <- get
return $! encodeFloat x y
CodePudding user response:
This BigFloat
seems to be not super-well-done. The problem lies here:
> floatDigits (0 :: BigFloat Prec500)
-9223372036854775808
(The correct answer should be 500.) I admit I don't fully understand why that is happening; but from the source, the way floatDigits
is computed is by taking the log of the precision -- but log
is a thing that happens on Double
s and Double
doesn't have enough precision to represent 1e-500
. So that method of computing the digit count seems doomed from the start.
Why do I say I don't fully understand? Well, I see the following in the source:
floatDigits (BF m _) =
floor $ logBase base $ recip $ fromRational $ precision m
From my reading, fromRational
should produce 0 :: Double
. But if that were true, the final value would be 0
, not minBound
:
> floor $ logBase 10 $ recip $ (0 :: Double) :: Int
0
So perhaps there is some dodgy rewriting going on or something.
Anyway, a proper implementation of this type should work differently -- e.g. by having a more informative class than Epsilon
that can report the precision as a base and exponent. (Another possible idea would be to have floatRadix
return recip precision
and floatDigits
return 1
, but that has some oddities around what should happen if the precision is not the reciprocal of a whole number.)