Anonymous | Login | Signup for a new account 2019-02-16 18:34 PST
 Main | My View | View Issues | Change Log | Roadmap | Summary | My Account

View Issue Details  Print ]
IDProjectCategoryView StatusDate SubmittedLast Update
0008311opensim[REGION] Script Functionspublic2018-04-03 10:272019-02-06 11:28
ReporterKayaker Magic
Assigned ToUbitUmarov
PrioritylowSeverityminorReproducibilityalways
StatusclosedResolutionfixed
PlatformLinux/MonoOSOpenSim 0.9.1.devOS Version0.9.1 dev
Product Versionmaster (dev code)
Target VersionFixed in Version
Summary0008311: llAcos and llAsin return NaN for values very close to 1.0
DescriptionI've been seeing a very rare error where llAcos returns a bad value that turns out to be NaN. This is the value it returns when you give it an input value that is out of bounds. But this was happening to me while using llAcos to calculate the angle between two unit vectors, a situation that was never supposed to be out of bounds.

Floating point numbers, even doubles, have a finite resolution. If you generated unit vectors at all possible angles, each component of the vector would be truncated by a different amount. The result is that some of the unit vectors will be slightly longer than 1.0 (about 2% of them), some less than 1.0 (about 34%) and most actually would have a length that was the floating point representation of 1.0 (about 62%, based on random samples).

In a small number of innocent calculations, the truncation error will result in unit vectors that are slightly longer than one and the dot product calculated will be slightly greater than one. This is an invalid value for cosine and if you call llAcos to find the angle, the result will be NaN.

A similar function in the same source fie, llAngleBetween, tests for >1.0 and assumes an angle of 0.0 in that case. This isn't the best solution for llAcos because you do want to detect truly invalid values.

So I propose testing in llAcos for a small range of values between 1.0 and 1.0000001, forgiving those invalid values and substituting an angle of 0.0. The truncation errors in single-precision vector lengths should always be less than 0.0000001. The same thing should be done with llAsin, although it will substitute +/-PI_BY_TWO. llAtan has no range limit and does not have this problem.
Steps To ReproduceThe following script gets NaN errors caused by floating point truncation of unit vectors. After applying the following patch, the errors are gone.

//The dot product of a unit vector with itself should always be 1.0
// never >1.0
integer times=0;
integer fails=0;
default
{
state_entry()
{
llSetTimerEvent(0.2);
}
timer()
{
times += 1;
//generate a random unit vector
vector a=llVecNorm(< llFrand(2.0)-1.0, llFrand(2.0)-1.0, llFrand(2.0)-1.0>);
vector b=a;
float cos=a*b; //the dot product of two vectors is the cosine of the angle between them
float angle = llAcos(cos);
if (angle!=angle) //this will be true if angle==NaN, according to IEEE
{ //why would llAcos return NaN? Because cos>1.000000000
fails += 1;
llOwnerSay("NaN! vector="+(string)a+ " failed "+(string)fails+" of "+(string)times+", "+(string)((integer)(100.0*(float)fails/(float)times))+"%");
}
llSetText((string)times,<0,1,0>,1.0);
}
}
Additional InformationFrom 356225736b2418b797fb71328f84288c98a233c2 Mon Sep 17 00:00:00 2001
From: Mike Higgins <mike@kayaker.net>
Date: Tue, 3 Apr 2018 09:47:55 -0700
Subject: [PATCH] put a test in llAcos and llAsin to prevent NaN on values
slightly above 1.0

---
.../Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs
index bac1468..47c2355 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs
@@ -860,6 +860,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api

public LSL_Vector llVecNorm(LSL_Vector v)
{
+//Console.WriteLine("Called llVecNorm =============================================");
return LSL_Vector.Norm(v);
}
@@ -5571,12 +5572,20 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
public LSL_Float llAcos(double val)
{
- return (double)Math.Acos(val);
+ const double TRIG_THRESHOLD = 0.0000001f; //Kayaker: truncation errors can result in values slightly above 1.0
+ if (Math.Abs(val) > 1f && (Math.Abs(val)-1f)<TRIG_THRESHOLD) //llAcos should forgive this and
+ return (double)0.0; //return 0 degrees as if this was +/-1.0
+ return (double)Math.Acos(val); //else return the normal value
}

public LSL_Float llAsin(double val)
{
+ const double TRIG_THRESHOLD = 0.0000001f; //Kayaker: truncation errors can result in values slighty above 1.0
+ if (val > 1f && (val-1f)<TRIG_THRESHOLD) //llAsin should forgive this and
+ return (double)ScriptBaseClass.PI_BY_TWO; //return pi/2 as if this was 1.0
+ if (val < -1f && (-val-1f)<TRIG_THRESHOLD)
+ return (double)-ScriptBaseClass.PI_BY_TWO; //or -pi/2 if this was -1.0
return (double)Math.Asin(val);
}

--
2.7.4
TagsNo tags attached.
Git Revision or version numberf83f7e18b6527ec4733d108898fee5539173cd99
Run Mode Grid (Multiple Regions per Sim)
Physics EngineODE
EnvironmentMono / Linux64
Mono Versiontrunk
ViewerFireStorm
Attached Files

 Relationships

 Notes melanie (administrator) 2018-04-03 10:43 Please attach the patch in mailbox format. You can create that format using git format-patch. UbitUmarov (administrator) 2018-04-03 11:05 you can see http://opensimulator.org/wiki/Submitting_code_to_OpenSim [^] for future. Sorry, can't accpet this patch. This a normal issue with limited precision math on cpus. you need to take this in consideration on your scripts, just where it is needed. and not only on trig functions. you can also modify the algoritm you use to avoid this issues. UbitUmarov (administrator) 2018-04-03 15:20 on branch httptests i did add a few auxiliar functions float osVecMagSquare(vector a) float osVecDistSquare(vector a, vector b)    returns the square of norm of vector or distance vector (a-b(, when expensive sqrt is not needed. float osAngleBetween(vector a, vector b)    returns the angle between a and b in radian -pi <= angle <= pi. all this could be done on lsl, but for completeness.. float osRound(LSL_Float value, LSL_Integer ndigits)    returns the value rounded to the number with a number if decimal places set by ndigits    ndigits = 0 is same as llRound(), max value is 15    note that ll*Say do llRound(value,6) by default so you can't test with them. this functions operate with doubles in current implementation but keep in mind that most operations read/write from/to floats on the framework, for example prim positions. let me know what i did mess up ;) UbitUmarov (administrator) 2018-04-03 17:23 oops float osAngleBetween(vector a, vector b) returns a angle between 0 and PI not -pi to pi UbitUmarov (administrator) 2018-07-11 10:55 alternative solution using new auxiliar ossl BillBlight (developer) 2019-02-06 11:28 Marked as Resolved but never closed, can be reopened if needed.

 Issue History Date Modified Username Field Change 2018-04-03 10:27 Kayaker Magic New Issue 2018-04-03 10:43 melanie Note Added: 0032627 2018-04-03 11:05 UbitUmarov Note Added: 0032628 2018-04-03 15:20 UbitUmarov Note Added: 0032629 2018-04-03 17:23 UbitUmarov Note Added: 0032630 2018-07-11 10:55 UbitUmarov Note Added: 0032780 2018-07-11 10:55 UbitUmarov Status new => resolved 2018-07-11 10:55 UbitUmarov Resolution open => fixed 2018-07-11 10:55 UbitUmarov Assigned To => UbitUmarov 2019-02-06 11:28 BillBlight Note Added: 0034389 2019-02-06 11:28 BillBlight Status resolved => closed