"线性"使用`normalize`时,包截断值接近0

时间:2015-01-12 21:11:30

标签: haskell linear

我花了几分钟时间调试一个跟踪到"线性"使用" Linear.normalize"时截断接近于零的值。具体来说,我正在采用非常小的三角形的交叉积并对结果进行标准化,令人惊讶的是,这种表现出错,直到我注意到错误并将十字产品乘以10000。

为什么这甚至是必要的?我该如何摆脱这种行为?

编辑:只是为了好玩,here is a video of the bug。请注意,当近似它的三角形数量足够大时,球体会失去颜色?是的,祝你好运调试......!

1 个答案:

答案 0 :(得分:2)

查看normalize的{​​{3}},您会发现它被定义为

-- | Normalize a 'Metric' functor to have unit 'norm'. This function
-- does not change the functor if its 'norm' is 0 or 1.
normalize :: (Floating a, Metric f, Epsilon a) => f a -> f a
normalize v = if nearZero l || nearZero (1-l) then v else fmap (/sqrt l) v
  where l = quadrance v

这意味着,如果您的积分值非常接近于0,那么您最终会得到错误的值。为避免这种情况,您可以编写自己的normalize函数,而无需将此检查作为

normalize' :: (Floating a, Metric f) => f a -> f a
normalize' v = fmap (/ sqrt l) v where l = quadrance v

运气好的话应该解决你的问题。

另一种解决方法可能是扩展您的值,执行计算,然后将它们缩小,例如

normalize' factor = (* factor) . normalize . (/ factor)

所以你可以打电话给

normalize' 10e-10 (V3 1e-10 2e-10 3e-10)
相反,但由于IEEE浮点数的存储方式,这很容易引入舍入误差。

编辑:正如cchalmers指出的那样,signorm已经将Linear.Metric实现为{{1}},所以请改用该函数。