Thread: Safeguarding against non-numerical input

  1. #1
    Registered User
    Join Date
    Oct 2012
    Posts
    99

    Safeguarding against non-numerical input

    What can I add to my function that will make sure only numerical input is added? This is not for homework.

    Code:
    float num_check (float num_entd)
    {
        while((num_entd>1000) || (num_entd<0))
          {
                printf("Please enter a valid weight between 0 and 1000 lbs\n");
                scanf("%f", &num_entd);
          }
    return num_entd;
    }
    Thanks

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    Read a line using fgets()

    Then parse the line using sscanf or strtod

    PS
    There is no need to pass num_entd as a parameter. Just make it a local, and return it.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    Stoned Witch Barney McGrew's Avatar
    Join Date
    Oct 2012
    Location
    astaylea
    Posts
    420
    Check the return value of scanf and handle the three cases appropriately.
    scanf(3) - Linux manual page

  4. #4
    Registered User
    Join Date
    Oct 2012
    Posts
    99
    Quote Originally Posted by Salem View Post
    Read a line using fgets()

    There is no need to pass num_entd as a parameter. Just make it a local, and return it.
    I am sending in a float value though. Are you saying I can just get rid of the (float num_entd) even though I'm sending in a float?

    Unfortunately, I don't yet know any of those commands

  5. #5
    Registered User
    Join Date
    Oct 2012
    Posts
    99
    Quote Originally Posted by Barney McGrew View Post
    Check the return value of scanf and handle the three cases appropriately.
    How about this?

    Code:
    float num_check (int tester, float num_entd)
    {
        
        while((num_entd>1000) || (num_entd<0) || tester=0)
          {
                printf("Please enter a valid weight between 0 and 1000 lbs\n");
                tester=scanf("%f", &num_entd);
          }
    return num_entd;
    }

  6. #6
    Registered User
    Join Date
    Oct 2012
    Posts
    99
    Ignore my wrong equals sign. tester==0

  7. #7
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    You have "... || tester=0", which assigns 0 to tester rather than checking whether tester is 0. You should be using ==. [edit] Fixed, I see. [/edit]

    How about something like this?
    Code:
    while(scanf("%f", &value) != 1 || value < 0 || value > 1000) {
        printf("Please try again.\n");
    }
    Could think about using a do-while loop as well.
    Code:
    int read;
    do {
        // ...
        read = scanf("%f", &value);
    } while(read != 1 || value < 0 || value > 1000);
    Or something.

    [edit] Why are you passing in the variables as arguments? Then the calling function has to provide values. Declare the variables locally.
    Code:
    void func() {
        int local_x;
        float local_y;
        // ...
    }
    [/edit]
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  8. #8
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    > I am sending in a float value though. Are you saying I can just get rid of the (float num_entd) even though I'm sending in a float?
    Why call the function with
    result = num_check (123.456);
    when that number has nothing to do with the result you get - it will be overwritten when you read something from the user.

    > Unfortunately, I don't yet know any of those commands
    Here's a tip - every time you see a new function, your first instinct should be to either use google, or read your manual pages.
    Saying "I don't know what foo does" everytime will be just frustrating for everyone involved.

    The reason for using fgets() to read a line, is simply that it's a nice neat way of keeping the input stream consistent.
    Code:
    int read;
    do {
        // ...
        read = scanf("%f", &value);
    } while(read != 1 || value < 0 || value > 1000);
    Because simple code like this runs into all sorts of trouble should scanf return zero (indicating that no conversion took place).

    scanf does NOT purge the input stream in any way, when presented with invalid data.
    So you have to resort to hackery like this -> FAQ > Flush the input buffer - Cprogramming.com
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  9. #9
    Stoned Witch Barney McGrew's Avatar
    Join Date
    Oct 2012
    Location
    astaylea
    Posts
    420
    Quote Originally Posted by gratiafide View Post
    How about this?

    Code:
    float num_check (int tester, float num_entd)
    {
        
        while((num_entd>1000) || (num_entd<0) || tester=0)
          {
                printf("Please enter a valid weight between 0 and 1000 lbs\n");
                tester=scanf("%f", &num_entd);
          }
    return num_entd;
    }
    That only handles the case where scanf returns 0. You need to check the case where it returns EOF (to indicate that there's no more data on stdin, or an error has occurred.) Since you've chosen an interactive design for your program, the following should be sufficient for your purposes:

    Code:
    #include <ctype.h>
    #include <stdio.h>
    
    static int discardline(FILE *stream)
    {
        int c;
    
        while ((c = getc(stream)) != '\n' && c != EOF)
            ;
        return c;
    }
    
    int readf(float *to, float min, float max)
    {
        const char *invalid = "invalid character: %c";
        int r;
    
        if ((r = scanf("%f", to)) == EOF)
            return EOF;
        if (!r) {
            warn(invalid, getchar());
            return discardline(stdin) == EOF ? EOF : 1;
        }
        while ((r = getchar()) != EOF && r != '\n')
            if (!isspace(r)) {
                warn(invalid, r);
                return discardline(stdin) == EOF ? EOF : 1;
            }
        if (*to < min) {
            warn("%f < %f", *to, min);
            return 1;
        }
        if (*to > max) {
            warn("%f > %f", *to, max);
            return 1;
        }
        return 0;
    }
    Using that function is fairly straightforward. It returns EOF if there's no more input, 0 if a value was successfully converted and stored in 'to', and 1 if there was an error on the line.
    Last edited by Barney McGrew; 12-04-2012 at 02:22 PM. Reason: Fixed a bug.

  10. #10
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Okay that's nice code but I don't know that it's simple to use. The reason I was trying to post simple code is because given the OP's passing in of variables that should be local, I thought they would need some intermediate code before seeing complete code to be able to understand what's going on.

    Anyway, basically you have a few errors that can happen. The user can enter non-numeric values, or the input stream can reach end-of-file. If you're reading data with scanf("%f") and you see non-numeric values, you have to skip over them before calling scanf() again (otherwise it will continue to fail); if you are reading input line-by-line then you can simply grab another line. In addition, you're checking whether the values entered are within a specific range.

    How many of these errors you want to handle is up to you. You've already seen Barney McGrew's example using scanf() and getchar() to do the character-by-character case. If you want to try reading lines you can do something like
    Code:
    char line[BUFSIZ];
    float value;
    while(fgets(line, sizeof line, stdin)) {
        if(sscanf(line, "%f", &value) == 1) {
            // found a float. Could also check for junk characters after the value if you wanted
            return value;
        }
        // nothing float-like on this line, grab another line
    }
    // end of file
    On end of file, you might exit the program or substitute some kind of default value. Or as in the previous code, return an error code and let the calling function handle it.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  11. #11
    Registered User
    Join Date
    Oct 2012
    Posts
    99
    I really appreciate all the effort put into these posts. I keep thinking there is an easy way to do these things and it usually seems it is not so easy. It will take me a while to digest all this but thanks so much!

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. numerical range of int and short
    By jackson6612 in forum C++ Programming
    Replies: 12
    Last Post: 06-03-2011, 03:06 AM
  2. How to determine if input is numerical
    By Fdisk in forum C Programming
    Replies: 8
    Last Post: 02-11-2010, 03:22 PM
  3. Numerical Integration
    By Crazed in forum C Programming
    Replies: 13
    Last Post: 03-03-2008, 03:01 PM
  4. Replies: 15
    Last Post: 01-11-2008, 03:57 PM
  5. numerical differentiation of data
    By lordbubonicus in forum C Programming
    Replies: 3
    Last Post: 10-25-2007, 03:01 PM