Home > Software design >  Haskell: How to define Binary instance for (BigFloat e)?
Haskell: How to define Binary instance for (BigFloat e)?

Time:08-24

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 Doubles 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.)

  • Related