haskell双精度下溢与修复

时间:2014-08-20 03:39:02

标签: haskell double-precision quickcheck repa

我使用repa编写了一些code calculating a distance matrix

distance :: Int -> Int -> Mat -> Double
distance aindx bindx arr = let a = slice arr (Any :. aindx :. All)
                               b = slice arr (Any :. bindx :. All)-
                               sqdiff = R.map (\x -> x*x) $ R.zipWith (-) a b 
                            in sqrt $ sumAllS sqdiff

buildDistanceMatrix :: Mat -> Mat 
buildDistanceMatrix m = let (Z :. height :. width) = R.extent m
                            cords = fromListUnboxed (Z :. (height * height) )  [ (x,y) | x <- [0..height-1], y <- [0..height-1]]
                            dist = R.smap (\(a,b) -> distance a b m) cords
                            dmat = R.reshape (Z :. height  :. height ) dist
                         in R.computeS dmat

似乎有效。但后来我添加了一个QuickCheck:

prop_distmat :: Double -> Bool
prop_distmat d = let dvec = [d,0,0,0]
                     dmat = R.fromListUnboxed (Z :. (2::Int) :. (2::Int)) dvec
                     dist = buildDistanceMatrix dmat
                  in (R.toList dist) == [0.0, d, d, 0.0 ]

换句话说,由距离D分开的两个点应该产生看起来像[0,D,D,0]的距离矩阵。在我的adhoc手动测试中,确实如此。但QuickCheck很快发现5.0e-324的距离产生的距离矩阵为[0,0,0,0]

distance matrix  *** Failed! Falsifiable (after 2 tests and 1074 shrinks):    
5.0e-324

这仅仅是因为双打的精确度?我是否需要钳制QuickCheck将发送的可能值?或者这是一个真正的错误?

2 个答案:

答案 0 :(得分:5)

您正在测试浮点数的相等性,通常应该避免这种情况(在任何语言中,这都不是特定于Haskell的)。你也可以通过大双打来获得无限大的溢出效果。 sqrt (x*x) == x即使对于那些没有上溢或下溢的双打也不成立。因此,您需要将==替换为差异最多为某个合理的epsilon,并限制可能的值(或检查属性中的溢出)。

答案 1 :(得分:1)

在应用平方根函数之前,很长时间地计算向量的L2范数可以给出不足或超过流量。我引用了一个知道的人:&#34; Fortran中两个规范的最强大计算有超过200行代码,但那是25年前的#34;。我建议搜索Fortran实现,然后使用可能出错的知识将其应用于您的Haskell实现。数字是棘手的;好消息是大约50年前大多数问题都可能在Fortran中得到解决。