2

So I have an assignment to figure out whether a number on the command line is either an integer or a double.

I have it mostly figured it out by doing:

sscanf(argv[x], "%lf", &d)

Where "d" is a double. I then cast it to an int and then subtract "d" with itself to check to see if it is 0.0 as such.

d - (int)d == 0.0

My problem is if the command line arguments contains doubles that can be technically classified as ints.

I need to classify 3.0 as a double whereas my solution considers it an int. For example initializing the program.

a.out 3.0

I need it to print out

"3.0 is a double"

However right now it becomes

"3 is an int." 

What would be a way to check for this? I did look around for similar problems which led me to the current solution but just this one edge case I do not know how to account for.

Thank you.

2
  • 2
    You could first use strtol to see if it's an integer. Check the end pointer to see if the entire string was consumed. If so, then it was an integer. Otherwise use strtod to try to parse it as a double, again checking to see if the entire string was consumed. Commented Sep 16, 2017 at 5:32
  • 2
    What about input like "1234567890123456789012345678901234567890"? which certainly will not fit in an int or even long long but will fit in a double. Consider strto... Commented Sep 16, 2017 at 8:59

4 Answers 4

4

For example, a way like this:

#include <stdio.h>

int main(int argc, char *argv[]){
    if(argc != 2){
        puts("Need an argument!");
        return -1;
    }

    int int_v, read_len = 0;
    double double_v;
    printf("'%s' is ", argv[1]);
    //==1 : It was able to read normally.
    //!argv[1][read_len] : It used all the argument strings.
    if(sscanf(argv[1], "%d%n", &int_v, &read_len) == 1 && !argv[1][read_len])
        puts("an int.");
    else if(sscanf(argv[1], "%lf%n", &double_v, &read_len) == 1 && !argv[1][read_len])
        puts("a double.");
    else
        puts("isn't the expected input.");
}
Sign up to request clarification or add additional context in comments.

3 Comments

Upvote. I brainlessly submitted a C++ answer. This is the correct method for C.
Alright I have not seen a lot of this used so let me see if I understand. I have not seen the use of %n but after looking it up it seems like it counts the symbols used before it reaches the %n. You use the number received from %n in !argv[1][read_len]. So you check if after de-referencing the command line pointer, you land on a null terminating character. If you do then it is an int otherwise its a double. Am I wrong or misunderstanding something here?
@IntroGrammer Yes, that kind of understanding is fine.
3

To test if a string will covert to a int and/or double (completely, without integer overflow, without undefined behavior), call strtol()/strtod(). @Tom Karzes

The trouble with a sscanf() approach is that the result is undefined behavior (UB) on overflow. To properly detect, use strtol()/strtod().

#include <errno.h>
#include <limits.h>
#include <stdbool.h>
#include <stdlib.h>

bool is_int(const char *src) {
  char *endptr;
  // Clear, so it may be tested after strtol().
  errno = 0;
  // Using 0 here allows 0x1234, octal 0123 and decimal 1234.
  // or use 10 to allow only decimal text.
  long num = strtol(src, &endptr, 0 /* or 10 */);

#if LONG_MIN < INT_MIN || LONG_MAX > INT_MAX
  if (num < INT_MIN || num > INT_MAX) {
    errno = ERANGE;
  }
#endif

  return !errno && endptr > src && *endptr == '\0';
}

bool is_double(const char *src) {
  char *endptr;
  // Clear, so it may be tested after strtod().
  strtod(src, &endptr);

  // In this case, detecting over/underflow IMO is not a concern.
  return endptr > src && *endptr == '\0';
}

5 Comments

Thanks for your input. I didn't mention it in my main post but currently I am required to use sscanf, my fault. I do want to see if I am understanding this correctly too however since I have not seen strtol/strtod. Strtol checks the start of an integer value and makes endptr point at the end of the integer. You check for overflow/underflow via that if statement in the middle. 3.0 will fail on this check here: "*endptr == '\0'" since endptr would not point at a null terminating character. Strtod does the same thing but it makes endptr point at the end of a double instead on an integer.
@IntroGrammer Almost. Overflow of a strtol() is checked via the !errno. Since an int may be narrower than a long (it could be the same), an additional check is needed if (num < INT_MIN ... in that case.
@IntroGrammer The sscanf() lacks specification on overflow (UB). Still rarely is that a problem. @BLUEPIXY is a good way to go for learner code. I prefer "%d %n" (with a space)` to pass text such as "123 " as rarely is trailing space a reason to invalidate.
Oh so errno automatically stores/detects(?) an overflow and the if statement is a secondary check since integers can be a long but not all longs can be integers. Correct?
@IntroGrammer errno is an effect strtol(), not an automatic store/detection. strtol() sets errno on overflow. strtol() does not clear errno otherwise, hence the need to clear it before calling strtol().
1

It is not entirely clear what the specific expectations are for your program, but it has at least something to do with the form of the input, since "3.0" must be classified as a double. If the form is all it should care about, then you should not try to convert the argument strings to numbers at all, for then you will run into trouble with unrepresentable values. In that case, you should analyze the character sequence of the argument to see whether it matches the pattern of an integer, and if not, whether it matches the pattern of a floating-point number.

For example:

int main(int argc, char *argv[]) {
    for (int arg_num = 1; arg_num < argc; arg_num++) {
        char *arg = argv[arg_num]; 
        int i = (arg[0] == '-' || arg[0] == '+') ? 1 : 0;  // skip any leading sign

        // scan through all the decimal digits
        while(isdigit(arg[i])) {
            ++i;
        }

        printf("Argument %d is %s.\n", arg_num, arg[i] ? "floating-point" : "integer");
    }
}

That makes several assumptions, chief among them:

  • the question is strictly about form, so that the properties of your system's built-in data types (such as int and double) are not relevant.

  • each argument will have the form of either an integer or a floating-point number, so that eliminating "integer" as a possibility leaves "floating-point" as the only alternative. If "neither" is a possibility that must also be accommodated, then you'll also need to compare the inputs that do not have integer form to a pattern for floating-point numbers, too.

  • only decimal (or smaller radix) integers need be accommodated -- not, for example, hexadecimal inputs.

Under those assumptions, particularly the first, it is not just unnecessary but counterproductive to attempt to convert the arguments to one of the built-in numeric data types, because you would then come to the wrong conclusion about arguments that, say, are not within the bounds of representable values for those types.

For example, consider how the program should classify "9000000000". It has the form of an integer, but supposing that your system's int type has 31 value bits, that type cannot accommodate a value as large as the one the string represents.

Comments

0
int main (int argc,char *argv[])
{
    if(argc==2)
    {
        int i;
        double d;
        d=atof(argv[1]);
        i=atoi(argv[1]);
        if(d!=i)
            printf("%s is a double.",argv[1]);
        else if(d==i)
            printf("%s is an int.",argv[1]);

    }
    else
        printf("Invalid input\n");
    return 0;
}

You must add #include <stdlib.h>

6 Comments

What if input is abc or abc.xyz?
Is not 1e2 a valid double without a .?
As @IntroGrammer wants to check either input is int or double. He doesn't bother about the validity of the number means he must input a valid number only.
@chux I don't know that 1e2 is a valid double or not. If it is a valid double then inform me. Accordingly, I will edit my answer.
With printf("%f %f %f\n",1e2, 1E2, 0x1p2);, --> 100.000000 100.000000 4.000000. C11 6.4.4.2 Floating constants and 7.22.1.3 The strtod, ... functions, goes into the details of what text qualifies.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.