我花了几分钟时间调试一个跟踪到"线性"使用" Linear.normalize"时截断接近于零的值。具体来说,我正在采用非常小的三角形的交叉积并对结果进行标准化,令人惊讶的是,这种表现出错,直到我注意到错误并将十字产品乘以10000。
为什么这甚至是必要的?我该如何摆脱这种行为?
编辑:只是为了好玩,here is a video of the bug。请注意,当近似它的三角形数量足够大时,球体会失去颜色?是的,祝你好运调试......!
答案 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}},所以请改用该函数。