Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

Discussion: isNumeric() function

Bosozoku Kato
insurrectionist midget
Join date: 16 Jun 2003
Posts: 452
01-22-2006 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.

CODE
// 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 NON-numeric. (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
01-22-2006 22:04
/15/24/83929/1.html
_____________________
i've got nothing. ;)
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
01-22-2006 23:55
why not
CODE

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
01-23-2006 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 non-numeric 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
01-23-2006 03:04
CODE

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
01-23-2006 07:12
Your code will accept "1-2" 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 non-numeric value.

Then llParseString2List(text, [], [".", "-"]) and make sure that the resulting list is 1-4 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
01-23-2006 08:09
Revised :)

CODE

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.

CODE

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...

CODE

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
01-24-2006 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.
CODE
if ( (integer)value < 1 )
llOwnerSay("Invalid value re.....
works just fine because any non-numeric 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
01-24-2006 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
01-24-2006 09:22
Would the following casting nightmare offer any improvement to catch things with numbers at the front?

CODE

if ( ((integer)a != 0) && (llStringLength(a) == llStringLength( (string) ((integer)a))) ....
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
01-24-2006 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
CODE

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, "-.e-0-.e+") != -1);
}


the ":" comment is on your signiture :o
_____________________
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
01-24-2006 09:48
From: Yumi Murakami
Would the following casting nightmare offer any improvement to catch things with numbers at the front?

CODE

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
01-24-2006 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.

CODE

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
01-24-2006 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
01-24-2006 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
01-24-2006 13:02
From: Strife Onizuka
the ":" comment is on your signiture :o
damn, Strife - u got good eyes, my man :D
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
01-24-2006 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
01-24-2006 14:32
Alright my end all be all isNumbers function.

CODE

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
01-24-2006 14:37
From: Strife Onizuka
Basicly intergers are more restricted.
And faster.
Rickard Roentgen
Renaissance Punk
Join date: 4 Apr 2004
Posts: 1,869
01-24-2006 14:39
if you just want to test to see if a string is an integer.

CODE

integer isInteger(string input)
{
return ((string)((integer)input) == input);
}
_____________________
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
01-24-2006 16:11
From: Rickard Roentgen
if you just want to test to see if a string is an integer.

CODE

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=LibraryFloat2Hex

EDIT: 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
05-01-2008 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:

CODE

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,[]),""));
}