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.