testing if a string is numeric

Status
Not open for further replies.

jasper

Active member
I am processing some strings received over serial input, and need to test if they are numeric.
"isNumeric" is a common function built into almost all programming languages I have used including SAP ABAP, VB, VBA, TSQL , Crystal reports and others.

I found this "isNumeric" function on the internet for arduino, it works on arduino boards but fails on the teensy at the "isdigit" function . Can anyone suggest an "isNumeric" function for Teensy?



// check a string to see if it is numeric
bool isNumeric(String str){
for(byte i=0;i<str.length();i++)
{
if(isDigit(str.charAt(i))) return true;
}
return false;
}


// the setup routine runs once when you press reset:
void setup() {
// initialize the digital pin as an output.
pinMode(13, OUTPUT);
}


bool isNumeric(String str){
for(byte i=0;i<str.length();i++)
{
if(isDigit(str.charAt(i))) return true;
}
return false;
}


// the loop routine runs over and over again forever:
void loop() {
digitalWrite(13, LOW);
if (isNumeric("aaaa") ) digitalWrite(13, HIGH);
if (isNumeric("12345")) digitalWrite(13, HIGH);


}
 
thanks jp the compilable sketch is shown below, ill do some testing . do you have any suggestions for handling decimal points? Currently i think it will reject a number containing a decimal point as non numeric.



boolean isDigit(char c) {
return ((c >='0') && (c <= '9'));
}

// check a string to see if it is numeric
bool isNumeric(String str){
for(byte i=0;i<str.length();i++)
{
if(isDigit(str.charAt(i))) return true;
}
return false;
}


// the setup routine runs once when you press reset:
void setup() {
// initialize the digital pin as an output.
pinMode(13, OUTPUT);
}



// the loop routine runs over and over again forever:
void loop() {
digitalWrite(13, LOW);
if (isNumeric("aaaa") ) digitalWrite(13, HIGH);
if (isNumeric("12345")) digitalWrite(13, HIGH);


}
 
are we confusing a C char array with a C++ class string with its methods?
the C char array (not a string per se), has isdigit(ch) and so on in the C library.

The string class is a bit heavy on use of RAM and cycles.
 
Indeed there is that.

But the IsNumeric() also has inverted logic. It should quit FALSE on the first non-numeric. The code above basically declares it Numeric if the last character in the string is Numeric;
 
Here is working code where the answer is "C" if you want a decimal point to be part of the number, the Arduino Library excludes that Character from isdigit()

@Jasper - you can rewrite with string to show the code growth over this:
Sketch uses 10,332 bytes (3%) of program storage space. Maximum is 262,144 bytes.
Global variables use 2,432 bytes (3%) of dynamic memory, leaving 63,104 bytes for local variables. Maximum is 65,536 bytes.

Code:
boolean isDigitC(char c) {
  return (((c >= '0') && (c <= '9')) || ( '.' == c) );
}
boolean isDigit(char c) {
  return (((c >= '0') && (c <= '9')));
}

// check a string to see if it is numeric
bool isNumeric(char *str) {
  for (byte i = 0; str[i]; i++)
  {
    Serial.print(str[i]);
    if (!isDigit(str[i])) return false;
  }
  return true;
}

// check a string to see if it is numeric and accept Decimal point
bool isNumericC(char *str) {
  for (byte i = 0; str[i]; i++)
  {
    Serial.print(str[i]);
    if (!isDigitC(str[i])) return false;
  }
  return true;
}

// check a string to see if it is numeric Using Arduino Lib code
bool isNumericB(char *str) {
  for (byte i = 0; str[i]; i++)
  {
    Serial.print(str[i]);
    if (!isdigit(str[i])) return false;
  }
  return true;
}


// the setup routine runs once when you press reset:
void setup() {
  Serial.begin(38400);
  delay(1000);  // DELAY for USB to re-open on reUpload
}



// the loop routine runs over and over again forever:
void loop() {
  char szTest[3][8] = { "aaaa", "12345", "123.45" };
  int ii;
  for ( ii = 0; ii < 3; ii++)
  {
    Serial.println(szTest[ii]);
    if (isNumeric(szTest[ii]) )
      Serial.println(" IsNumeric");
    else Serial.println(" Not_Numeric");
    if (isNumericB(szTest[ii]) )
      Serial.println(" IsNumericB");
    else Serial.println(" Not_NumericB");
    if (isNumericC(szTest[ii]) )
      Serial.println(" IsNumericC");
    else Serial.println(" Not_NumericC");
    delay(300);
  }
  Serial.println(" Delay . . .");
  delay(5000);
}

Sample output:
aaaa
a Not_Numeric
a Not_NumericB
a Not_NumericC
12345
12345 IsNumeric
12345 IsNumericB
12345 IsNumericC
123.45
123. Not_Numeric
123. Not_NumericB
123.45 IsNumericC
 
Last edited:
Thanks guys good job. Just 3 more checks and we are going where no other isDigit has gone before :
Allow negative numbers.
Allow only one decimal point.
Allow only one minus sign
 
Go for it - I'll look at it if you post. In my quickest pseudocode with the special cases and orderings you mentioned. something like this assuming the char index I is handled after Int I=0:
maybe make "int IsNumericD()" (from isNumericC()) return 0=no, -1=yes, else return the string index if it was a number so far but a '.' was processed {index past it and return index).
if '-' == char:: yes I++
I = IsNumericD( & char );
if 0 == I:: not a number
if -1 == I:: good Number
else if !IsNumeric( & char ) then not a number else good Number
 
Last edited:
That code is deceptively wrong. It seems isdigit() in intended to look at the first character so that it is known passing to a number conversion func() will have at least one digit to chew. In this case walking the whole string to verify - you may as well convert it yourself as you go - or just call the number conversion func() and see if you like the number you get back. Getting the chars from Serial this is a way to force corruption detection perhaps.
 
@jasper - did you look into your next steps?

I fleshed out my Pseudo code - kept it all inside IsNumeric() since I was there. Only one problem left - what is the real numeric value of "-." [ or "." or "-" ]. Added a fix in the non-Debug version.

BTW: I went to Arduino isdigit() as I saw a sample saying it is faster than the hand coded C variant, not sure if it hauls in anything - but it does work as expected.

Code:
// check a string to see if it is numeric and accept Decimal point
bool isNumeric(char * str) {
  byte ii = 0;
  bool RetVal = false;
  if ( '-' == str[ii] )
    ii++;
  while (str[ii])
  {
    if ( '.' == str[ii] ) {
      ii++;
      break;
    }
    if (!isdigit(str[ii])) return false;
    ii++;
    RetVal = true;
  }
  while (str[ii])
  {
    if (!isdigit(str[ii])) return false;
    ii++;
    RetVal = true;
  }
  return RetVal;
}

bool isNumericDebug(char * str) {
  byte ii = 0;
  Serial.print(str[ii]);
  if ( '-' == str[ii] ) {
    ii++;
    Serial.print(str[ii]);
  }
  while (str[ii])
  {
    if ( '.' == str[ii] ) {
      ii++;
      Serial.print(str[ii]);
      break;
    }
    if (!isdigit(str[ii])) return false;
    ii++;
    Serial.print(str[ii]);
  }
  while (str[ii])
  {
    if (!isdigit(str[ii])) return false;
    ii++;
    Serial.print(str[ii]);
  }
  return true;
}

// the setup routine runs once at powerup or reset:
void setup() {
  Serial.begin(38400);
  delay(1000);  // DELAY for USB to re-open on reUpload
}

// the loop routine runs over and over again forever:
void loop() {
#define CNT_STRINGS  12
  char szTest[CNT_STRINGS][8] = { "aaaa", "12345", "123.45", "1-2345", "-123.45", "1.23.45", "123.4-5", "123.", "123.-", "-.", ".", "-" };
  int ii;
  for ( ii = 0; ii < CNT_STRINGS; ii++)
  {
    Serial.println(szTest[ii]);
    if (isNumericDebug(szTest[ii]) )
//    if (isNumeric(szTest[ii]) )
      Serial.println(" IsNumeric");
    else Serial.println(" Not_Numeric");
    delay(300);
  }
  Serial.println(" Delay . . .");
  delay(5000);
}

And sample output:
aaaa
a Not_Numeric
12345
12345 IsNumeric
123.45
123.45 IsNumeric
1-2345
1- Not_Numeric
-123.45
-123.45 IsNumeric
1.23.45
1.23. Not_Numeric
123.4-5
123.4- Not_Numeric
123.
123. IsNumeric
123.-
123.- Not_Numeric
-.
-. IsNumeric
.
. IsNumeric
-
- IsNumeric
 
Last edited:
thanks defragster and others, i have used it in my dronescan code so if you see dronescan making money then be sure to ask for some of it!
not to be ungrateful or anything but i would like : "-." "." and "-" to be flagged as NOT numeric.
This is because the distance sensors we are reading often return "--.-" when they cant read the distance because its out of range or something. We would like this to be flagged as not numeric please.
 
Hope it works for you - Glad I felt intrigued to play with the code . . . I took the 'self' out of 'self-help' . . .

not to be ungrateful or anything but i would like : "-." "." and "-" to be flagged as NOT numeric

You should look a bit more closely at my post and the code -
Added a fix in the non-Debug version.
 
thanks defragster,

(if there was a "like" button i would click it, if there were points to be awarded you would get max.).
 
Glad to help - the final output is below.

Is this your site: http://www.dronescan.co/

Is there a Teensy in that?

aaaa
Not_Numeric
12345
IsNumeric
123.45
IsNumeric
1-2345
Not_Numeric
-123.45
IsNumeric
1.23.45
Not_Numeric
123.4-5
Not_Numeric
123.
IsNumeric
123.-
Not_Numeric
-.
Not_Numeric
.
Not_Numeric
-
Not_Numeric
 
thanks. correct.
there isnt a teensy in DroneScan yet, been using an arduino mega based board but hopefully converting to teensy, the board controls the barcode scanner, reads the laser , ultrasonic and infrared range finder sensors, communicates to the ground station, receives PPM signals from the pilot and prepares and sends ppm signals to the flight control unit (like APM or Pixhawk or one of those) thereby assisting the pilot to control the drone.
Its all working (to some extent), just not happy with the current board we are using.
 
Status
Not open for further replies.
Back
Top