Tagoria
Tagoria

Reputation: 38

How to test a string which is a number for >INT_MAX or <INT_MIN on a 32bit µC?

A customer text based protocol is used and it allows to set int32 values for some specific parameters by sending them e.g. from PC to a 32bit µC. I need to check if the received parameter is in range [INT_MIN, INT_MAX]. I need to generate a failure if the number is outside of this range. Simply casting to int and compare that with [INT_MIN, INT_MAX] would fail.

My current idea is:
1. casting the string to number,
2. casting the number to string,
3. compare the received and casted string from 2. ,

If both equal the number in the string should be a valid int32 value.

Are there any other suggetions or ideas? Thx

Upvotes: 0

Views: 1062

Answers (2)

chux
chux

Reputation: 153602

How to test a string which is a number for >INT_MAX or <INT_MIN ...

check if the received parameter is in range [INT_MIN, INT_MAX]

"casting the string to number," and "casting the number to string," will not work as an int test. Code needs to convert.

long int strtol(const char *nptr, char **endptr, int base) readily does this. @PSkocik

#include <ctype.h>
#include <limits.h>
#include <stdlib.h>

bool test_int(const char *s, int *int_result) {
  char *endptr;   // Location where conversion stopped
  errno = 0;      // Need to clear, to test for overflow later
  long n = strtol(s, &endptr, 0);
  if (errno == ERANGE) {
    return false; // Outside long range
  }

  // In case int is narrower than long
  #if LONG_MIN < INT_MIN || LONG_MAX > INT_MAX
  if (n < INT_MIN || n > INT_MAX) {
      return false; // Outside int range
  }
  #endif

  if (s == endptr) {
    return false; // No conversion
  }
  
  // Perhaps allow trailing white-space?
  while (isspace((unsigned char) *endptr)) {
    endptr++;
  }

  if (*endptr != '\0') {
    return false; // Extra junk at the end
  }

  *int_result = (int) n;
  return true;
}

As part of a protocol parser, with various ranged integers, consider a general purpose signed test:

bool test_integer(const char *s, intmax_t *integer, intmax_t mn, intmax_t mx) {
  char *endptr; 
  errno = 0;
  *integer = strtoimax(s, &endptr, 0);
  if (errno == ERANGE || *integer < mn || *integer > mx) {
    *integer = (*integer < mn) ? mn : mx;
    errno == ERANGE;
    return false; // Outside intmax_t range
  }

  while (isspace((unsigned char) *endptr)) {
    endptr++;
  }

  if (s == endptr || *endptr != '\0') {
    return false; // No conversion or junk at end
  }
  
  return true;
}

// Sample usage for int
intmax_t im;
if (test_integer(s, &im, INT_MIN, INT_MAX))  {
  int i = (int) im;
  ...

Upvotes: 2

bitmask
bitmask

Reputation: 34636

For a fixed number of compile-time numbers this check can be done efficiently directly on the string. More so, given that you can directly tell by the first character whether or not it is negative or not.

After you branch into checking for MAX_INT or MIN_INT, two numbers you can convert to a string cmp either at design time or once at runtime, you can easily do the following:

  1. Check if the length of the input exceeds the length of cmp ->fail
  2. If it is shorter ->pass
  3. If it is equal compare both strings component wise while the characters match. The first character in the input string that compares strictly greater than cmp results in a ->fail
  4. Otherwise the input is fine and you can convert it.

Yes, this adds an additional loop and n+1 branches in the worst case you if you validate the string in this pass, you can omit all branches in the conversion step, so you will only have to pay one additional branch.

Upvotes: 0

Related Questions