Normalizing vectors with very small components.

D1189cc75544eea31979c55b45fa72ef
0
broli86 101 Aug 08, 2008 at 07:31

How do I handle this gracefuly ?

vector2d *vector2d_normalize(vector2d *a)
{
    double length;

    length = vector2d_length(a);
    if (length != 0.0)
    {
        a->x /= length;
        a->y /= length;
    }
    return (a);
}

But what if I want to calculate some vector even when the components are very small ?? The above approach won’t normalize the vector and simply leave it as it is.

7 Replies

Please log in or register to post a reply.

6673a7d3bfd3d1db5e05c5676cc040b6
0
Goz 101 Aug 08, 2008 at 10:18

So when you debug it does length come out as 0.0?

If so .. there is your problem. vector2d_length may be using a very poor square root …

D1189cc75544eea31979c55b45fa72ef
0
broli86 101 Aug 08, 2008 at 12:34

@Goz

So when you debug it does length come out as 0.0? If so .. there is your problem. vector2d_length may be using a very poor square root …

Yes the length does come out as 0.0 and I have caught it many times using the assert macro. What do you mean by poor square root ? I’m using the sqrt function from math.h

340bf64ac6abda6e40f7e860279823cb
0
_oisyn 101 Aug 08, 2008 at 12:44

@Goz

If so .. there is your problem. vector2d_length may be using a very poor square root …

I think it’s not the square root that’s the problem, it’s the quadratation of the components before doing the square root that causes trouble. A float has an 8-bit exponent ranging from -127 to 128. Squaring values between 0 and 1 effectively halves the exponent, so as soon as the values get smaller than around 2-64 (\~1e-20) you’re in trouble.

If you want to be able to normalize vectors with such small components, first scale them up, then normalize them.

Something like

vector normalize(vector v)
{
    const float smallvalue = 1.0e-20f;
    const float largevalue = 1.0e20f;
    if ((abs(v.x) < smallvalue && abs(v.y) < largevalue) ||
        (abs(v.y) < smallvalue && abs(v.x) < largevalue))
    {
        v.x *= smallvalue;
        v.y *= smallvalue;
    }

    // do the rest of the normalization here
}

.edit: made it somewhat more robust.

99f6aeec9715bb034bba93ba2a7eb360
0
Nick 102 Aug 08, 2008 at 12:44

vector2d_length computes the length as sqrt(x * x + y * y). So if x and/or y are very small then squaring them could result in underflow (zero), or denormal numbers (using only part of the normal precision). It’s possible that sqrt turns denormals into zero too.

The solution: scale both x and y up by a power of two (say 2\^20), then normalize.

4c85bbf0fe52dd82315ff56c8797ef91
0
Blaxill 101 Aug 08, 2008 at 15:29

@.oisyn

vector normalize(vector v)
{
    const float smallvalue = 1.0e-20f;
    const float largevalue = 1.0e20f;
    if ((abs(v.x) < smallvalue && abs(v.y) < largevalue) ||
        (abs(v.y) < smallvalue && abs(v.x) < largevalue))
    {
        v.x *= smallvalue;// here and
        v.y *= smallvalue;// here
    }

    // do the rest of the normalization here
}

Shouldn’t the multiplies be ‘largevalue’

D1189cc75544eea31979c55b45fa72ef
0
broli86 101 Aug 08, 2008 at 15:39

@Blaxill

Shouldn’t the multiplies be ‘largevalue’

Yes I think it should be largevalue and we can apply the same logic for 3d vectors as well:

vector *vector_normalize(vector *a)
{
    double length;    
    const double smallvalue = 1.0e-20;
    const double largevalue = 1.0e20;

     if ((fabs(a->x) < smallvalue && fabs(a->y) < largevalue && fabs(a->z) < largevalue) ||
         (fabs(a->y) < smallvalue && fabs(a->x) < largevalue && fabs(a->z) < largevalue) ||
         (fabs(a->z) < smallvalue && fabs(a->x) < largevalue && fabs(a->y) < largevalue))
    {
        a->x *= largevalue;
        a->y *= largevalue;
        a->z *= largevalue;
    }

    length = vector_length(a);
    if (length != 0.0)
    {
      a->x /= length;
      a->y /= length;
      a->z /= length;      
    }

    return (a);
}
340bf64ac6abda6e40f7e860279823cb
0
_oisyn 101 Aug 08, 2008 at 16:26

@Blaxill

Shouldn’t the multiplies be ‘largevalue’

uh yes indeed B)