Anonymous | Login | Signup for a new account | 2018-07-21 04:43 PDT |

Main | My View | View Issues | Change Log | Roadmap | Summary | My Account |

View Issue Details [ Jump to Notes ] | [ Issue History ] [ Print ] | ||||||||

ID | Project | Category | View Status | Date Submitted | Last Update | ||||

0008311 | opensim | [REGION] Script Functions | public | 2018-04-03 10:27 | 2018-07-11 10:55 | ||||

Reporter | Kayaker Magic | ||||||||

Assigned To | UbitUmarov | ||||||||

Priority | low | Severity | minor | Reproducibility | always | ||||

Status | resolved | 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 | ||||||||

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 <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 ============================================="); 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)<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) { m_host.AddScriptLPS(1); + 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 | ||||||||

Tags | No tags attached. | ||||||||

Git Revision or version number | f83f7e18b6527ec4733d108898fee5539173cd99 | ||||||||

Run Mode | Grid (Multiple Regions per Sim) | ||||||||

Physics Engine | ODE | ||||||||

Environment | Mono / Linux64 | ||||||||

Mono Version | trunk | ||||||||

Viewer | FireStorm | ||||||||

Attached Files | |||||||||

Notes | |

(0032627) melanie (administrator) 2018-04-03 10:43 |
Please attach the patch in mailbox format. You can create that format using git format-patch. |

(0032628) 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. |

(0032629) 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 ;) |

(0032630) 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 |

(0032780) UbitUmarov (administrator) 2018-07-11 10:55 |
alternative solution using new auxiliar ossl |

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 |

Copyright © 2000 - 2012 MantisBT Group |