繁体   English   中英

Haskell:如何为 (BigFloat e) 定义二进制实例?

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

如何为 Data.Number.BigFloat 定义二进制实例?

我通过以下方式为 LongDouble 定义了一个实例:

instance Binary LongDouble where
    put d = put (decodeFloat d)
    get   = do
        x <- get
        y <- get
        return $! encodeFloat x y

但是尝试以下方法不起作用:

instance Binary (BigFloat e) where
    put d = put (decodeFloat d )
    get   = do
        x <- get
        y <- get
        return $! encodeFloat x y

GHC 给出此错误消息:

/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

如果我为 e 提供特定类型,例如 Prec500,我会收到以下消息:

• 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)’

并且使用 FlexibleInstances 编译,但不会产生正确编码的数字。

以下内容也可以编译,但也不能正确编码:

instance Epsilon e => Binary (BigFloat e) where
    put d = put (decodeFloat d )
    get   = do
        x <- get
        y <- get
        return $! encodeFloat x y

这个BigFloat似乎做得不是很好。 问题出在这里:

> floatDigits (0 :: BigFloat Prec500)
-9223372036854775808

(正确答案应该是 500。)我承认我不完全理解为什么会这样。 但从源头来看,计算floatDigits的方式是获取精度的对数——但log是发生在Double s 上的事情,而Double没有足够的精度来表示1e-500 因此,这种计算数字计数的方法似乎从一开始就注定要失败。

为什么我说我不完全理解? 好吧,我在源代码中看到以下内容:

floatDigits (BF m _) =
    floor $ logBase base $ recip $ fromRational $ precision m

根据我的阅读, fromRational应该产生0:: Double 但如果这是真的,最终值将是0 ,而不是minBound

> floor $ logBase 10 $ recip $ (0 :: Double) :: Int
0

因此,也许正在进行一些狡猾的重写或其他事情。

无论如何,这种类型的正确实现应该以不同的方式工作——例如,通过比Epsilon提供更多信息的 class 可以将精度报告为基数和指数。 (另一个可能的想法是让floatRadix返回recip precision ,而floatDigits返回1 ,但是如果精度不是整数的倒数,这会发生一些奇怪的事情。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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