Discussion: isNumeric() function

Bosozoku Kato
insurrectionist midget
Join date: 16 Jun 2003
Posts: 452

01222006 17:28
Edit:I trimmed the routine a little, now instead of doing string comparisons on every character I just typecast to an integer and only do string comparions if the value of the int is zero. Edit2: Added check if string passed contains just a single minus sign "", reordered nested if's more logically. // isNumeric() checks a given string to see if it IS numeric // It allows for negative values and floats. // isNumeric() returns FALSE if the given string isn't numeric // else it returns TRUE. // Input (string) ending with a period is considered NONnumeric. (eg: "12.") integer isNumeric(string sInput) { integer nLen = llStringLength(sInput)  1; // len, zero based string sChr; integer nDecimalCount = 0; integer nLoopCount = 0; // initially check first char for "" (negative, ignore (increment loopcounter) if (llGetSubString(sInput, 0, 0) == "") { nLoopCount = 1; if (nLoopCount > nLen) return FALSE; // string passed is a single character "" } while (nLoopCount <= nLen) { sChr = llGetSubString(sInput, nLoopCount, nLoopCount); if ((integer)sChr == 0) { if (sChr == ".") { ++nDecimalCount; // if more than 1 ".", OR last character is a ".", false! if (nDecimalCount > 1  nLoopCount == nLen) return FALSE; } else if (sChr != "0") { return FALSE; } } ++nLoopCount; } return TRUE; // if we got this far, then it's numeric... }
_____________________
float llGetAFreakingRealTimeStampSince00:00:00Jan11970();

Nada Epoch
The Librarian
Join date: 4 Nov 2002
Posts: 1,423

Original Thread
01222006 22:04
_____________________
i've got nothing.

Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887

01222006 23:55
why not isNumeric(string a) { if((float)a) return 1; if((integer)a) return 1; return (llGetSubString(a,0,0) == "0"); }
comment on Bosozoku: you can't have ":" in function names, it's not supported in the lex file.
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river.  Cyril Connolly
Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence.  James Nachtwey

Bosozoku Kato
insurrectionist midget
Join date: 16 Jun 2003
Posts: 452

01232006 01:48
Strife, I consider you one of SL's Geniuses :), but I'm not sure what you're example does. First, it doesn't even have a return type (gonna halt the compiler right there), then you're returning either an int or string. Also there's a comparison in the last return...this is for? Not sure what you're returning either, what's 1 equate to, TRUE? if ((integer)a) return 1;  what if a == "1b" (integer)"1b" == 1, which I assume would be considered "TRUE" in LSL, and thus return 1, whatever that means. But "1b" is definitely not numeric. In LSL these will evalute as TRUE: if ((integer)"1b") if ((float)"1b") These evaluate as FALSE: if ((integer)"b1") if ((float)"b1") So that borks your comparisons if a string passed starts with a number, but contains nonnumeric characters. Anywho, I'm confused, which is pretty much my normal state of being. :) Also, there is no ":" in my function's name. Don't know where you see that. Bos
_____________________
float llGetAFreakingRealTimeStampSince00:00:00Jan11970();

Rickard Roentgen
Renaissance Punk
Join date: 4 Apr 2004
Posts: 1,869

01232006 03:04
list numbers = ["0","1","2","3","4","5","6","7","8","9"];
integer isNumeric(string input) { if (input == "") return FALSE; integer i; for (i = 0; i < 10; i += 5) { input = llDumpList2String(llParseString2List(input, llList2List(numbers, i, i + 4), [".",""]), ""); } return (input == ""  input == ""  input == "."  input == "."); }
best I could come up with . There's another method breaking it at the decimal if there is one, then converting the two sections to integers and back to strings, the problem is if a section has a numerical value greater than 2147483647 (2^31) the function will return false because the conversion rounds down to the max possible int value.

Argent Stonecutter
Emergency Mustelid
Join date: 20 Sep 2005
Posts: 20,263

01232006 07:12
Your code will accept "12" as a number.
How about using llParseString2List(text, [".", "", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"],[])? If the result isn't the empty list, your string contains a nonnumeric value.
Then llParseString2List(text, [], [".", ""]) and make sure that the resulting list is 14 elements long, and if it's 4 elements the first is "" and the third is ".", and if it's 3 elements the second or third isn't "".
Then apply Strife's code.

Rickard Roentgen
Renaissance Punk
Join date: 4 Apr 2004
Posts: 1,869

01232006 08:09
Revised list numbers = ["0","1","2","3","4","5","6","7","8","9"];
integer isNumeric(string input) { // Check for a null string and reject if found. if (input == "") return FALSE; // Check for negative numbers and allow them. if (llGetSubString(input, 0, 0) == "") input = llGetSubString(input, 1, llStringLength(input)); // Filter out all numeric characters. integer i; for (i = 0; i < 10; i += 5) { input = llDumpList2String(llParseString2List(input, llList2List(numbers, i, i + 4), ["."]), ""); } // Return FALSE if any none numeric character remains, other than a single ".". return (input == ""  input == "."); }
Also, since leading and trailing spaces won't effect it's conversion to a number, the following allows for whitespace padding. list numbers = ["0","1","2","3","4","5","6","7","8","9"];
integer isNumeric(string input) { // Check for leading and trailing spaces and allow them. list trimmed_input = llParseString2List(input, [" "], []); if (llGetListLength(trimmed_input) > 1) { return FALSE; } else { input = llList2String(trimmed_input, 0); }
// Check for negative numbers and allow them. if (llGetSubString(input, 0, 0) == "") input = llGetSubString(input, 1, llStringLength(input));
// Check for a null string and reject if found. if (input == "") return FALSE; // Filter out all numeric characters. integer i; for (i = 0; i < 10; i += 5) { input = llDumpList2String(llParseString2List(input, llList2List(numbers, i, i + 4), ["."]), ""); } // Return False if anything none numeric character is found, other than a single ".". return (input == ""  input == "."); }
and if you need to allow hex... integer isNumeric(string input) { list numbers = ["0","1","2","3","4","5","6","7","8","9"]; input = llToLower(input);
// Check for leading and trailing spaces and allow them. list trimmed_input = llParseString2List(input, [" "], []); if (llGetListLength(trimmed_input) > 1) { return FALSE; } else { input = llList2String(trimmed_input, 0); } // Check for negative numbers and allow them. if (llGetSubString(input, 0, 0) == "") input = llGetSubString(input, 1, llStringLength(input)); // Check and allow for hex values. integer isHex = llGetSubString(input, 0, 1) == "0x"; if (isHex) { input = llGetSubString(input, 2, 1); numbers += ["a","b","c","d","e","f"]; }
// Check for a null string and reject if found. if (input == "") return FALSE; // Filter out all numeric characters. integer i; for (i = 0; i < 16; i += 8) { input = llDumpList2String(llParseString2List(input, llList2List(numbers, i, i + 7), ["."]), ""); } // Return False if anything none numeric character is found, other than a single ".". return (input == ""  (input == "." && !isHex)); }

Ben Bacon
Registered User
Join date: 14 Jul 2005
Posts: 809

01242006 04:23
some nice code and great ideas here  but readers should be aware that in many cases zero is not a valid option. In these cases a simple typecast would be easier and muuuuch faster. e.g. with my rental vendor you can leave the "FreeWeeksEvery" value out of the config notecard (or just comment it out) if u don't want to offer discount weeks. If you do specify it, however, it must be a positive number. if ( (integer)value < 1 ) llOwnerSay("Invalid value re..... works just fine because any nonnumeric text is cast to 0, triggering the error message, as does "0" itself, while "3 for now, later on I'll make it 5" is successfully handled as 3. When you do need to handle zero as a valid option, or when your code has to be strict about its input, use Bosozoku's or Rickard's code to do the job.

Rickard Roentgen
Renaissance Punk
Join date: 4 Apr 2004
Posts: 1,869

01242006 09:00
hehe, ya, kinda depends on what you need to check for. A typcast is much simpler, but it won't actually tell you if you have text in your string. (integer)"1sdf325" would return 1, so to the typecast check it would appear to be a number.

Yumi Murakami
DoIt!AttachTheEarOfACat!
Join date: 27 Sep 2005
Posts: 6,860

01242006 09:22
Would the following casting nightmare offer any improvement to catch things with numbers at the front? if ( ((integer)a != 0) && (llStringLength(a) == llStringLength( (string) ((integer)a))) ....

Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887

01242006 09:40
the advantage of the typecast is you don't have to look for hex strings. "0xDEAD" is a valid hex string and typecast will result in 57005. It depends what your looking for. if memory serves me llParseString* allows for 8 of each EDIT: UGGG this doesn't work properly on floats, fixing it isNumber(string a) { if(a == "") return 0; integer b = llGetSubString(a = llToLower(a), 0, 1) == "0x"; list c = ["8","9"]; if(b) c += ["a","b","c","d","e","f"]; a = (string)llParseString2List( (string)llParseString2List(a, c, []), ["0","1","2","3","4","5","6","7"], []); if(b) return (a == "x"); else if(llGetSubStringIndex( return (llSubStringIndex(a, ".e0.e+") != 1); }
the ":" comment is on your signiture
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river.  Cyril Connolly
Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence.  James Nachtwey

Rickard Roentgen
Renaissance Punk
Join date: 4 Apr 2004
Posts: 1,869

01242006 09:48
From: Yumi Murakami Would the following casting nightmare offer any improvement to catch things with numbers at the front? if ( ((integer)a != 0) && (llStringLength(a) == llStringLength( (string) ((integer)a))) ....
the problem with comparing the lengths of converted and reconverted strings is that 1.11111111 will be cut down to 1.111111 after having gone throught the conversion process. float precision will kill you.

Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887

01242006 11:13
it's not going to be fast, i haven't tested it, but it should recognize valid floats in scientific notation as well as integers. integer isFloat(string a) { integer b = llStringLength(a); integer c = llStringLength(a)  1; while(llGetSubString(a,b,b) == " " && b) ++b; while(llGetSubString(a,c,c) == " " && (c + 1)) c; a = llToLower(llDeleteSubString(a, c + 1, b  1)); if(b = (llGetSubString(a,0,0) == "")) a = llDeleteSubString(a,0,0); if(a != "") { if(llGetSubString(a,0,1) == "0x") return !llSubStringIndex("x.",(string)llParseString2List( (string)llParseString2List(a, ["8","9","a","b","c","d","e","f"], []), ["0","1","2","3","4","5","6","7"], [])); else { c = llStringLength(a); do { integer d = llSubStringIndex("+.e0123456789",llGetSubString(a,c,c)); if(d < 0) return 0; if(!(b & 0x7C) && (d > 3))//digits before "." b = b  0x2; else if(!(b & 0x7C) && d == 2)//decimal b = b  0x4; else if(((b & 0x74) == 0x4) && (d > 3))//digits after "." b = b  0x8; else if(!(b & 0x70) && (b & 0xA) && (d == 3))//e (e does not require ".") b = b  0x10; else if(((b & 0x70) == 0x10) && (d < 2))//minus or plus signs b = b  0x20; else if(((b & 0x10) == 0x10) && (d > 3))//digits after e b = b  0x40; else return 0; }while(++c); return (b & 0xA) != 0; } } return 0; }
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river.  Cyril Connolly
Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence.  James Nachtwey

Rickard Roentgen
Renaissance Punk
Join date: 4 Apr 2004
Posts: 1,869

01242006 11:42
can you convert a scientific notation string to a float in lsl?

Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887

01242006 11:48
scientific notation and hex
so... (float)"1e1" == 10 and (float)"0x1000.1000" == 4096.0625
the hex part i just learned today myself, was using a simple testscript for checking the float values.
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river.  Cyril Connolly
Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence.  James Nachtwey

Ben Bacon
Registered User
Join date: 14 Jul 2005
Posts: 809

01242006 13:02
From: Strife Onizuka the ":" comment is on your signiture damn, Strife  u got good eyes, my man

Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887

01242006 13:07
i've renamed the function, to isFloat, as floats and integers are parsed differently by the (string) typecast.
floats can do everything integers can and more. Basicly intergers are more restricted.
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river.  Cyril Connolly
Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence.  James Nachtwey

Rickard Roentgen
Renaissance Punk
Join date: 4 Apr 2004
Posts: 1,869

01242006 14:32
Alright my end all be all isNumbers function. integer isNumeric(string input) { list numbers = ["0","1","2","3","4","5","6","7","8","9"]; input = llToLower(input); string exponent;
// Check for leading and trailing spaces and allow them. list trimmed_input = llParseString2List(input, [" "], []); if (llGetListLength(trimmed_input) > 1) { return FALSE; } else { input = llList2String(trimmed_input, 0); } // Check for negative numbers and allow them. if (llGetSubString(input, 0, 0) == "") input = llGetSubString(input, 1, llStringLength(input)); // Check and allow for hex values. integer isHex = llGetSubString(input, 0, 1) == "0x"; if (isHex) { input = llGetSubString(input, 2, 1); numbers += ["a","b","c","d","e","f"]; } else { // Check for scientific notation. if (llGetSubstringIndex(input, "e") > 0) { list sci_notation = llParseString2List(input, ["e"], []);
if (llGetListLength(sci_notation) != 2) return FALSE;
input = llList2String(sci_notation, 0); exponent = llList2String(sci_notation, 1); } }
// Check for a null string and reject if found. if (input == "") return FALSE; // Filter out all numeric characters. integer i; for (i = 0; i < 16; i += 8) { input = llDumpList2String(llParseString2List(input, llList2List(numbers, i, i + 7), ["."]), ""); }
// Filter out all numeric characters from the exponent component of a scientific notation number. if (exponent != "") { for (i = 0; i < 16; i += 8) { exponent = llDumpList2String(llParseString2List(exponent , llList2List(numbers, i, i + 7), []), ""); } } // Return False if anything none numeric character is found, other than a single ".". return ((input == ""  input == ".") && exponent == ""); }

Argent Stonecutter
Emergency Mustelid
Join date: 20 Sep 2005
Posts: 20,263

01242006 14:37
From: Strife Onizuka Basicly intergers are more restricted. And faster.

Rickard Roentgen
Renaissance Punk
Join date: 4 Apr 2004
Posts: 1,869

01242006 14:39
if you just want to test to see if a string is an integer. integer isInteger(string input) { return ((string)((integer)input) == input); }

Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887

01242006 16:11
From: Rickard Roentgen if you just want to test to see if a string is an integer. integer isInteger(string input) { return ((string)((integer)input) == input); }
true but that doesn't catch hex integers "0xDEAD" here is a function for encoding floats as hexadecimal strings you can find it here http://secondlife.com/badgeo/wakka.php?wakka=LibraryFloat2HexEDIT: i have discovered that LSL supports C99 style Hex Floats the wiki page above implements this, it's a great fast function for encoding floats. It's the holy grail of functions for save float transport. It's fast and uses little space.
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river.  Cyril Connolly
Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence.  James Nachtwey

Very Keynes
LSL is a Virus
Join date: 6 May 2006
Posts: 484

05012008 07:20
I was tackling a similar problem and came up with a solution similar Rickard Roentgen without realising, so for the sake completeness the full thread is here: RFC  isInt() / isHex() / isFloat() / isNumeric() And the specific function that fits this thread is: integer isNumeric(string val) { integer point; list mask = ["8","9"]; if(~(point = llSubStringIndex(val = llStringTrim(llToLower(val),STRING_TRIM),"."))) val = llGetSubString(val,0,point  1)+llGetSubString(val,point + 1, 1); if(~(point = llSubStringIndex(val,"e"))) val = llGetSubString(val,0,point  1); if(!llSubStringIndex(val,"")) val = llGetSubString(val,1,1); if(!llSubStringIndex(val,"0x")) {val = llGetSubString(val,2,1); mask = ["8","9","a","b","c","d","e","f"];} val = llDumpList2String(llParseString2List(val,["0","1","2","3","4","5","6","7"],[]),""); return !llStringLength(llDumpList2String(llParseString2List(val,mask,[]),"")); }
