3

I've spent a few minutes debugging a problem that tracked down to "Linear" truncating values that are close to zero when using "Linear.normalize". Specifically, I was taking the cross product of very small triangles and normalizing the result, which, surprisingly, behaved wrongly until I noticed what was wrong and multiplied the cross product by 10000.

Why is that even necessary? How can I get rid of that behavior?

Edit: just for fun, here is a video of the bug. Notice that the sphere loses the color when the number of triangles approximating it is big enough? Yes, good luck debugging that...!

duplode
  • 33,731
  • 7
  • 79
  • 150
MaiaVictor
  • 51,090
  • 44
  • 144
  • 286
  • 1
    What values were you using? It could be due to the IEEE floating point format instead of the linear package's math. – bheklilr Jan 12 '15 at 21:13
  • Scratch that, the normalize function uses the `Linear.Epsilon.nearZero` function, it is definitely truncating your values. – bheklilr Jan 12 '15 at 21:15

1 Answers1

3

Looking at the source for normalize, you'll see that it's defined as

-- | 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

What this means is that if the magnitude of your points is really close to 0 you're going to end up with the wrong value. To avoid this you can write your own normalize function without this check as

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

And with any luck it should solve your problem.

Another way around this might be to scale up your values, perform the computations, then scale them back down, something like

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

So you might call

normalize' 10e-10 (V3 1e-10 2e-10 3e-10)

instead, but this could easily introduce rounding errors due to how IEEE floating point numbers are stored.

EDIT: As cchalmers points out this is already implemented as signorm in Linear.Metric, so use that function instead.

bheklilr
  • 53,530
  • 6
  • 107
  • 163
  • 1
    Your `normalize'` is just `signorm` (also in `Linear.Metric`). – cchalmers Jan 12 '15 at 21:58
  • @cchalmers I'll point that out then, I have no previous experience with the `linear` package, so I'm just going off of reading the source and poking around a bit. – bheklilr Jan 12 '15 at 21:59
  • Interesting, just to make sure - what is exactly the meaning of signorm? Is it doing something else (with the signals, maybe?) or is it exactly just a normalize without truncation? – MaiaVictor Jan 12 '15 at 23:23