Question

I found a problem with the 3D Vector normalization. It seems that it happens only with some specific numbers. Every time I call a normalization function, the Vector gets changed.

Example code:

Vector3 v1(-0.965090453265, -0.210381150246, 0.156014174223);

std::cout.precision(15);
v1.normalize();
std::cout << v1 << std::endl;
v1.normalize();
std::cout << v1 << std::endl;
v1.normalize();
std::cout << v1 << std::endl;
v1.normalize();
std::cout << v1 << std::endl;

The output:

-0.965090334415436 -0.210381120443344 0.156014159321785
-0.965090453624725 -0.210381150245667 0.156014174222946
-0.965090334415436 -0.210381120443344 0.156014159321785
-0.965090453624725 -0.210381150245667 0.156014174222946

The normalize function:

void Vector3::normalize()
{
    if ( length() == 0 )
        return;

    Vector3 x = *this;
    float sqr = x.x * x.x + x.y * x.y + x.z * x.z;
    *this = x * (1.0f / std::sqrt(sqr));
}

Is this a problem with the float precision, or something is wrong with my code? How to avoid this problem?

Was it helpful?

Solution

EDIT: Just figured out why I couldn't reproduce the problem.

If I change x /= len in my code below to x *= ((t) 1.) / len then I end up with exactly what you gave for floats, i.e. inconsistent answers beyond the first six digits (which you should not be trusting float for anyway):

-0.965090334415436 -0.210381120443344 0.156014159321785
-0.965090453624725 -0.210381150245667 0.156014174222946
-0.965090334415436 -0.210381120443344 0.156014159321785
-0.965090453624725 -0.210381150245667 0.156014174222946
-0.965090334415436 -0.210381120443344 0.156014159321785

As expected, doubles are still correct up to the 15th digit:

-0.965090340387771 -0.210381125639766 0.156014155975542
-0.965090340387771 -0.210381125639766 0.156014155975542
-0.965090340387771 -0.210381125639766 0.156014155975542
-0.965090340387771 -0.210381125639766 0.156014155975542
-0.965090340387771 -0.210381125639766 0.156014155975542

Still, the float result is always consistent to the first six digits.

ORIGINAL POST:

This seems to be "just a floating point precision issue", since single-precision floats only give you up to 6-9 decimal digits of precision. I've written code to check this and my results don't seem nearly as bad as yours.

#include <iostream>
#include <cmath>

template <typename t>
class Vector3
{

public:
    t x;
    t y;
    t z;

    Vector3 (t x, t y, t z) :
        x (x),
        y (y),
        z (z)
    {}

    void normalize()
    {
        t len = std::sqrt(x * x + y * y + z * z);

        if (len != 0.)
        {
            x /= len;
            y /= len;
            z /= len;
        }
    }

    void println()
    {
        std::cout << x << " " << y << " " << z << std::endl;
    }

};

int main(int argc, char ** argv)
{
    std::cout.precision(15);

    Vector3<float> v(-0.965090453265, -0.210381150246, 0.156014174223);

    for (int i = 0; i < 5; ++i)
    {
        v.normalize();
        v.println();
    }

    return 0;

}

Output:

-0.965090334415436 -0.210381120443344 0.156014159321785
-0.965090394020081 -0.210381135344505 0.156014174222946
-0.965090394020081 -0.210381135344505 0.156014174222946
-0.965090394020081 -0.210381135344505 0.156014174222946
-0.965090394020081 -0.210381135344505 0.156014174222946

Changing Vector3<float> to Vector3<double> gives consistent results up to the 15th digit:

-0.965090340387771 -0.210381125639766 0.156014155975542
-0.965090340387771 -0.210381125639766 0.156014155975542
-0.965090340387771 -0.210381125639766 0.156014155975542
-0.965090340387771 -0.210381125639766 0.156014155975542
-0.965090340387771 -0.210381125639766 0.156014155975542

Notice that even where float results stopped changing, the digits are not actually correct beyond the first seven.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top