[英]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.