angle between vectors better style
Hi ! What are you thinking about calculating the angle between two vectors by :
angle = atan2( cross(v1, v2).len(), dot(v1, v2) );
instead of:
angle = acos( dot(v1, v2)/(v1.len()* v2.len() ) );
?
This can be little slower but give a precisive calculations and avoid of range check ( for acos )
I don''t understand what range check you are talking about for acos. As far as I can see your solution is ok, but I don''t see any advantage with it.
The main advantage is a precision of calculations of cause.
The precision of "acos" fall near 0 degree angles, cause the derivatife goes to infinity.
The range check is the the check before applying "acos" function to prevent some math exeption
the acos( 1.0000001 ) - give the exeption.( 0.0000001 - an error of length calculations)
Even if you try the easy experiment:
float angle = random()*PI;
float cos_a = cos(angle);
float sin_a = sin(angle);
float restored1 = acos(cos_a);
float restored2 = atan2(sin_a, cos_a);
float e1 = fabs(angle - restored1);
float e2 = fabs(angle - restored2);
e1 alwais greater( or some time equal ) than the e2.
But in real calculations we have some error of rounding when calculating (for example) the length of vectors, and this error grow in a "acos" but not in "atan2".
The precision of "acos" fall near 0 degree angles, cause the derivatife goes to infinity.
The range check is the the check before applying "acos" function to prevent some math exeption
the acos( 1.0000001 ) - give the exeption.( 0.0000001 - an error of length calculations)
Even if you try the easy experiment:
float angle = random()*PI;
float cos_a = cos(angle);
float sin_a = sin(angle);
float restored1 = acos(cos_a);
float restored2 = atan2(sin_a, cos_a);
float e1 = fabs(angle - restored1);
float e2 = fabs(angle - restored2);
e1 alwais greater( or some time equal ) than the e2.
But in real calculations we have some error of rounding when calculating (for example) the length of vectors, and this error grow in a "acos" but not in "atan2".
You have quite a mixure of things here. Personally I feel there is a fundamental flaw. Analyzing statements in isolation lead to very broad statements, but here you are making broad statements about relatively insignificant differances. That is both in terms of performance and error propogation. The big question is what is the rest of the program doing. I have trouble seeing what you need the angle between two vectors for. That leads to even more problem seeing why you would do it so much in a program that the relative performance of the two would be significant. With no idea what you are going to use that angle for I certainly have no idea how the error introduced is going to grow.
quote:Original post by minorlogic
The main advantage is a precision of calculations of cause.
The precision of "acos" fall near 0 degree angles, cause the derivatife goes to infinity.
For small angles, yes, precision will be lost.
quote:The range check is the the check before applying "acos" function to prevent some math exeption
the acos( 1.0000001 ) - give the exeption.( 0.0000001 - an error of length calculations)
Do have a real world example of where dot(a,b) > len(a)*len(b) using floating point calculations?
quote:Original post by LilBudyWizer
....
With no idea what you are going to use that angle for I certainly have no idea how the error introduced is going to grow.
Yes, thanks, i know when i use it and usualy i don''t use any trigonomerty.
It is common problem: performance <-> precision.
It intresting for me, how some other people use this calculations. I don''t know the theory of error propagation, but i have the experimental results.
for example the :
http://www.hadron.org/~hatch/rightway.php
trying to introduce stable calculations.
i feel to make a performance tests with both versions... and post here
//-----------------------------
quote:
Do have a real world example of where dot(a,b) > len(a)*len(b) using floating point calculations?
Yes, it is very common problem, just play with vector normalization or with very short vectors ( some len about 0.0000001f ).
quote:Original post by minorlogicquote:
Do have a real world example of where dot(a,b) > len(a)*len(b) using floating point calculations?
Yes, it is very common problem, just play with vector normalization or with very short vectors ( some len about 0.0000001f ).
Then I guess you have to do the test, just like atan2 tests for the sign of its operands and if the second operand is zero.
the simple test i made is a very starange for me, may be somebody will help me with this :
//---------------------------------------
inline float standart_angle_between( const vector3& v1, const vector3& v2){
float ls = v1.len_squared()* v2.len_squared();
if( ls > 0.0000001f ) // just avoid of zero division
return (float)acos( dot(v1, v2)/(float)sqrt( ls ) );
else
return 0;
}
inline float stable_angle_between( const vector3& v1, const vector3& v2){
return (float)atan2 ( cross(v1, v2).len(), dot(v1, v2) );
}
float test_angle_between()
{
float R1 = 0, R2 = 0 ;
vector3 varr1[ 100 ];
vector3 varr2[ 100 ];
for( int i = 0; i < 100; i++){ // RandomVector() - return the unit vector
varr1 = RandomVector()*(1.0f + random()*100.f) ;<br> varr2 = RandomVector()*(1.0f + random()*100.f);<br> }<br><br> {<br> XX_PROFILE_BLOCK("stable_angle_between" );<br> for( int i = 0; i < 100; i++){<br> R1 += stable_angle_between(varr1, varr2 );<br> }<br> }<br> {<br> XX_PROFILE_BLOCK("standart_angle_between" );<br> for( int i = 0; i < 100; i++){<br> R2 += standart_angle_between( varr1, varr2 );<br> }<br> }<br> return R1 + R2;<br>}<br>//—————————————<br><br>Here is my results :<br>standart_angle_between - 0.07 milliseconds<br>stable_angle_between - 0.03 milliseconds<br>Pentium3 900 Mhz. (MSVC 7.0)<br><br>What does it mean ? Where im i wrong ?<br>Can somebody repeat the test ?<br><br>
//---------------------------------------
inline float standart_angle_between( const vector3& v1, const vector3& v2){
float ls = v1.len_squared()* v2.len_squared();
if( ls > 0.0000001f ) // just avoid of zero division
return (float)acos( dot(v1, v2)/(float)sqrt( ls ) );
else
return 0;
}
inline float stable_angle_between( const vector3& v1, const vector3& v2){
return (float)atan2 ( cross(v1, v2).len(), dot(v1, v2) );
}
float test_angle_between()
{
float R1 = 0, R2 = 0 ;
vector3 varr1[ 100 ];
vector3 varr2[ 100 ];
for( int i = 0; i < 100; i++){ // RandomVector() - return the unit vector
varr1 = RandomVector()*(1.0f + random()*100.f) ;<br> varr2 = RandomVector()*(1.0f + random()*100.f);<br> }<br><br> {<br> XX_PROFILE_BLOCK("stable_angle_between" );<br> for( int i = 0; i < 100; i++){<br> R1 += stable_angle_between(varr1, varr2 );<br> }<br> }<br> {<br> XX_PROFILE_BLOCK("standart_angle_between" );<br> for( int i = 0; i < 100; i++){<br> R2 += standart_angle_between( varr1, varr2 );<br> }<br> }<br> return R1 + R2;<br>}<br>//—————————————<br><br>Here is my results :<br>standart_angle_between - 0.07 milliseconds<br>stable_angle_between - 0.03 milliseconds<br>Pentium3 900 Mhz. (MSVC 7.0)<br><br>What does it mean ? Where im i wrong ?<br>Can somebody repeat the test ?<br><br>
hmmm... if the precision is so important to you where are you using floats and not doubles?
>>Do have a real world example of where dot(a,b) > len(a)*len(b) using floating point calculations?
I have had this problem on several occasions... and its a pain in the ass, took me ages to track it down...
but back to the original post...
angle = atan2( cross(v1, v2).len(), dot(v1, v2) );
seems like a long way to do it, its quite nice how you can calculate it all in just one line... but if speed is the main concern i don''t think its very efficient...
i know of two ways to get an angle, this is how i do it:
angle = dot(v1,v2)/(v1.len()*v2.len());
if (angle > 1)
angle = 1;
else if (angle < -1)
angle = -1;
angle = acos(angle);
This will give you an angle between 0 and 180 degrees, if the direction is important (clockwise/anti-clockwise) then you need to also use the cross product of the two and find out what direction its pointing... this is the same as you mentioned, but you can also do it like this:
angle = cross(v1, v2).len()/(v1.len()*v2.len());
if (angle > 1)
angle = 1;
else if (angle < -1)
angle = -1;
angle = asin(angle);
This will give you an angle between 90 and -90 (well it won''t actually cause cross(v1,v2).len() will always be positive, but if you do it manually and keep the sign then it will).. and this avoids the problem you talked about low precision for angles near to 0...
>>Do have a real world example of where dot(a,b) > len(a)*len(b) using floating point calculations?
I have had this problem on several occasions... and its a pain in the ass, took me ages to track it down...
but back to the original post...
angle = atan2( cross(v1, v2).len(), dot(v1, v2) );
seems like a long way to do it, its quite nice how you can calculate it all in just one line... but if speed is the main concern i don''t think its very efficient...
i know of two ways to get an angle, this is how i do it:
angle = dot(v1,v2)/(v1.len()*v2.len());
if (angle > 1)
angle = 1;
else if (angle < -1)
angle = -1;
angle = acos(angle);
This will give you an angle between 0 and 180 degrees, if the direction is important (clockwise/anti-clockwise) then you need to also use the cross product of the two and find out what direction its pointing... this is the same as you mentioned, but you can also do it like this:
angle = cross(v1, v2).len()/(v1.len()*v2.len());
if (angle > 1)
angle = 1;
else if (angle < -1)
angle = -1;
angle = asin(angle);
This will give you an angle between 90 and -90 (well it won''t actually cause cross(v1,v2).len() will always be positive, but if you do it manually and keep the sign then it will).. and this avoids the problem you talked about low precision for angles near to 0...
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement