MantisBT - opensim View Issue Details ID Project Category View Status Date Submitted Last Update 0008311 opensim [REGION] Script Functions public 2018-04-03 10:27 2019-02-06 11:28 Reporter Kayaker Magic Assigned To UbitUmarov Priority low Severity minor Reproducibility always Status closed Resolution fixed Platform Linux/Mono OS OpenSim 0.9.1.dev OS Version 0.9.1 dev Product Version master (dev code) Target Version Fixed in Version Git Revision or version number f83f7e18b6527ec4733d108898fee5539173cd99 Run Mode Grid (Multiple Regions per Sim) Physics Engine ODE Environment Mono / Linux64 Mono Version trunk Viewer FireStorm Summary 0008311: llAcos and llAsin return NaN for values very close to 1.0 Description I'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 Reproduce The 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 Information From 356225736b2418b797fb71328f84288c98a233c2 Mon Sep 17 00:00:00 2001 From: Mike Higgins 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 =============================================");              m_host.AddScriptLPS(1);              return LSL_Vector.Norm(v);          } @@ -5571,12 +5572,20 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api          public LSL_Float llAcos(double val)          {              m_host.AddScriptLPS(1); - 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) 1f && (val-1f) 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

Notes
 (0032627) melanie 2018-04-03 10:43
 Please attach the patch in mailbox format. You can create that format using git format-patch.
 (0032628) UbitUmarov 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.
 (0032629) UbitUmarov 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 ;)
 (0032630) UbitUmarov 2018-04-03 17:23
 oops float osAngleBetween(vector a, vector b) returns a angle between 0 and PI not -pi to pi
 (0032780) UbitUmarov 2018-07-11 10:55
 alternative solution using new auxiliar ossl
 (0034389) BillBlight 2019-02-06 11:28
 Marked as Resolved but never closed, can be reopened if needed.