简体   繁体   中英

Haskell: How to define Binary instance for (BigFloat e)?

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM