Category talk:LSL Float

From Second Life Wiki
Revision as of 03:57, 20 December 2013 by Omei Qunhua (talk | contribs) (Revised float-string validation solution)
Jump to navigation Jump to search

Something Seems Off

Simple Script <lsl> default {

   state_entry()
   {
       llOwnerSay((string)((float)"3.402823466E+38"));
   }

} </lsl> In Mono It Says: 340282300000000000000000000000000000000.000000

In LSL Is Says: 340282346638528859811704183484516925440.000000

Shouldn't Both Give The Same? Adi 20:51, 15 July 2009 (UTC)

You can report it as a bug on Jira. The two values are however identical when read as floats. It's a feature of floating point precision types; the precision is at the top, not the bottom of the value. -- Strife (talk|contribs) 20:30, 17 July 2009 (UTC)
I believe some inconsistency is to be expected, though the difference does seem a little odd. In any event Mono's floats are definitely different as they support infinity where LSO will throw a Math Error beyond the maximum or minimum values, the differences in general are a good thing and it's unlikely it cause any real faults between LSO and Mono scripts. I would definitely report it on the JIRA though, and maybe add the issue to Kelly Linden's Scripting Triage so it can investigated to find out if it's a true float error, or if it's a bug in the way Mono casts it back to a string (perhaps it's rounding by significant figures rather than decimal places, or intentionally removing "error"?), is worth investigating further at least.
-- Haravikk (talk|contribs) 10:16, 23 October 2010 (UTC)
Infinity support was added intentionally to Mono. -- Strife (talk|contribs) 14:23, 24 October 2010 (UTC)

inf/nan handling in MONO

Last I checked both of those will crash a MONO script when used in math, just not on evaluation. I'll try and remember to confirm that. and see what they do when fed to a few functions.... might give me an excuse to reopen that jira Joshua nuked =S
-- Void (talk|contribs) 16:42, 30 May 2012 (PDT)

I only know about it because 3.403E+38 gets rounded up to infinity in Mono (it came up in a discussion recently). I thought I should document the situation. I'm pretty sure *nothing* supports it but I'm looking forward to finding a clever way to do so with FUI. -- Strife (talk|contribs) 21:04, 30 May 2012 (PDT)

nevermind, looks like the change was either reverted or I got it wrong.... and the functions I tested that took float/vector seem to treat them as 0, although I do note that color actually saves the actual value. Like you, I actually wanted them to print the extended signaling. although i wanted it for crushing keys into rotation data types, and storage of integers into float parameters.
-- Void (talk|contribs) 20:30, 3 June 2012 (PDT)

Validation of float strings

The first example of a validation function:-

integer isValidFloat(string s) { return (string)((float)s) != (string)((float)("-" + llStringTrim(s, STRING_TRIM_HEAD))); }

seems to be very limited, in that the process examines the leading float-compatible characters only. Hence a string such as 1w2x3y will be treated as a valid float. I've yet to check the second validation example.

Secondly, be aware that under LSO, ( "abc" != "def") returns -1, whereas under Mono it returns 1. So the function result can't be tested for TRUE. Omei Qunhua 03:52, 19 December 2013 (PST)

In LSO, != was strcmp. I tried to get them to keep it the same in Mono but they wouldn't.
As to validation:
We set the bar pretty low for validation. To just test the beginning of the string. LSL as you already know stops parsing a float and returns what it currently has when it encounters a bad character. So basically we set validation to: Is LSL going to find something or nothing? Also LSL is not sensitive to leading spaces, at least not for spaces. This validator is really fast.
Say you want to validate more carefully, ok, down the rabbit hole we go. We assume you do not want left over characters, does that include leading and trailing whitespace?
Writing a complete validator for LSL would be difficult but not impossible, especially now. You could use JSON to do the number parsing for decimal floats, but you would still need to write a parser for hexfloats. That is the advantage of the current validator, it's format agnostic and fast. Having to test all characters is not fast, it's going to be quite slow.
That said I've had a few ideas on how to do it faster (but still not fast). -- Strife (talk|contribs) 11:04, 19 December 2013 (PST)
Sure, sensible validation of a float string is tricky, but I just think the current offering is pointless. People's eyes will light up when they see a proposed solution on the Wiki, and then be lifted to the heavens as they realise it does next to nothing :) Omei Qunhua 14:03, 19 December 2013 (PST)
It's very non-trivial. However, I am currently working on a solution. I should have it up later tonight. It won't be pretty but it will work. -- Strife (talk|contribs) 14:51, 19 December 2013 (PST)

Here's a compiled and tested solution:- It accepts "inf", "-inf" and "nan" ... and not "infant" etc. which would clearly by balderdash It makes no attempt to validate hex floats. My view is that validation is required where the input has been provided by a human, and who is going to want to encode a hex float by hand? (Oh! Silly me - Strife would!!) I've removed the code that was computing the value of the various sub components.

<lsl> // Validate a string containing a float value // Does not handle hex floats (!) // Compiled and tested. Omei Qunhua. Dec 2013

integer ValidateFloatString(string ss) {

   if (ss == "inf" || ss == "-inf" || ss == "nan")       return TRUE;
   integer flagDP;
   integer flagEXP;
   integer num = llStringLength(ss);
   integer x;
   string char;
   for ( ; x < num; ++x)           // Examine the string byte by byte left to right
   {
       char = llGetSubString(ss, x, x);
       if (char == "-" || char == "+")
       {
           if (x)  return FALSE;
       }
       else if (char == ".")
       {
           if (flagEXP)           return FALSE;      // '.' cannot come after 'E' / 'e'
           if (++flagDP > 1)      return FALSE;      // cannot have more than 1 '.'
       }
       else if (char == "E" || char == "e")
       {
           if (!flagDP)           return FALSE;        // E/e must have been preceeded by a '.'
           if (++flagEXP > 1)     return FALSE;        // ... and no other 'E'
           char = llGetSubString(ss, x+1, x+1);        // Examine the following character
           if (char == "+" || char == "-")        ++x;          
           if (x != (num - 3) )     return FALSE;
       }
       else if ( (string) ( (integer) char) != char)       return FALSE;         // effectively  checking for 0 thru 9
   }
   return TRUE;

} </lsl> Omei Qunhua 03:57, 20 December 2013 (PST)

It's a good start but it doesn't include signed infinities, hex floats and it's currently case sensitive. "E" also does not have to be followed by a sign.-- Strife (talk|contribs) 21:13, 19 December 2013 (PST)
Also since LSL doesn't care about how things end "infinity" and "infinite" and even "infant" are all parsed as "inf". I'm not ever sure how we want to validate that. -- Strife (talk|contribs) 21:34, 19 December 2013 (PST)

<lsl> integer allFloat(string in) { string lower = llStringTrim(llToLowerCase(in), STRING_TRIM_BOTH); if(llGetSubString(lower, 0, 0) == "-") lower = llDeleteSubString(lower, 0, 0); if(~llListFindList(["inf", "nan"], [lower])) return TRUE;//yeah I'm not solving this one either. if(lower) { list split; integer length; string dec = lower; if(llGetSubString(lower, 0, 1) == "0x"){//hex split = llParseStringKeepNulls(llDeleteSubString(lower, 0, 1), [], ["p+","p-","p"]); length = llGetListLength(split); if((length | 2) != 3) return FALSE; //must be 1 or 3 if(length == 3){ dec = llList2String(split, 2); if(dec == "") return FALSE; } lower = llList2String(split, 0); if(~llListFindList(["", "."], [lower])) return FALSE; split = llParseStringKeepNulls(lower, ["."], []); length = llGetListLength(split); if((length | 2) != 3) return FALSE; //must be 1 or 3 lower = (string)split; if((string)llParseString2List((string)llParseString2List(lower, ["0","1","2","3","4","5","6","7"], []), ["8","9","a","b","c","d","e","f"], [])) return FALSE; if(dec == "") return TRUE; } else { //decimal split = llParseStringKeepNulls(llDeleteSubString(lower, 0, 1), [], ["e+","e-","e"]); length = llGetListLength(split); if((length | 2) != 3) return FALSE; //must be 1 or 3 if(length == 3){ dec = llList2String(split, 2); if(dec == "") return FALSE; } lower = llList2String(split, 0); if(~llListFindList(["", "."], [lower])) return FALSE; split = llParseStringKeepNulls(lower, ["."], []); length = llGetListLength(split); if((length | 2) != 3) return FALSE; //must be 1 or 3 dec += (string)split; } if(dec){ return "" == (string)llParseString2List((string)llParseString2List(lower, ["0","1","2","3","4","5","6","7"], []), ["8","9"], []); } } return FALSE; }</lsl>

My solution (not compiled or debugged) --Strife (talk|contribs) 21:50, 19 December 2013 (PST)