haskell double precision underflow with repa
我已经编写了一些代码,使用 repa 计算距离矩阵:
1 2 3 4 5 6 7 8 9 10 11 12 | 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:
1 2 3 4 5 |
换句话说,被距离 D 分开的两点应该产生一个看起来像 [0,D,D,0] 的距离矩阵。在我的临时手动测试中,确实如此。但 QuickCheck 很快发现 5.0e-324 的距离会产生 [0,0,0,0]
的距离矩阵
1 2 |
这仅仅是因为 Doubles 的精度吗?我是否需要限制 QuickCheck 将发送的可能值?或者这是一个真正的错误?
您正在测试浮点数是否相等,这通常应该避免(在任何语言中,这不是 Haskell 特定的)。你也会得到一个无限大的双打。并且
天真地计算向量的 L2 范数可能会在应用平方根函数之前很久就给出下溢或溢流。我引用知道的人的话:"Fortran 中对两个范数最稳健的计算有超过 200 行代码,但那是 25 年前的事了"。我建议搜索 Fortran 实现,然后使用可能出错的知识将其应用于您的 Haskell 实现。数字很??棘手;好消息是,大约 50 年前,大多数问题可能都在 Fortran 中得到了解决。