indentify if argument is char or float

Discussion in 'C Programming' started by yaniv.dg@gmail.com, Aug 5, 2006.

  1. Guest

    hi all,
    i building an equition with float type now,what function should i use
    in order to identify is someone enter a char value by mistake?
     
    , Aug 5, 2006
    #1
    1. Advertising

  2. said:

    > hi all,
    > i building an equition with float type now,what function should i use
    > in order to identify is someone enter a char value by mistake?


    Capture the input as a string.
    Use strtod to convert to floating-point form.
    Use the ability strtod gives you to identify the position of the first
    character that could not be used in the conversion.

    --
    Richard Heathfield
    "Usenet is a strange place" - dmr 29/7/1999
    http://www.cpax.org.uk
    email: rjh at above domain (but drop the www, obviously)
     
    Richard Heathfield, Aug 5, 2006
    #2
    1. Advertising

  3. Guest

    hi richard,
    do you know maybe on a function C that i can use,its just need o check
    if its a float value or non float number

    Richard Heathfield כתב:
    > said:
    >
    > > hi all,
    > > i building an equition with float type now,what function should i use
    > > in order to identify is someone enter a char value by mistake?

    >
    > Capture the input as a string.
    > Use strtod to convert to floating-point form.
    > Use the ability strtod gives you to identify the position of the first
    > character that could not be used in the conversion.
    >
    > --
    > Richard Heathfield
    > "Usenet is a strange place" - dmr 29/7/1999
    > http://www.cpax.org.uk
    > email: rjh at above domain (but drop the www, obviously)
     
    , Aug 5, 2006
    #3
  4. said:
    > Richard Heathfield said:
    >> said:
    >>
    >> > hi all,
    >> > i building an equition with float type now,what function should i use
    >> > in order to identify is someone enter a char value by mistake?

    >>
    >> Capture the input as a string.
    >> Use strtod to convert to floating-point form.
    >> Use the ability strtod gives you to identify the position of the first
    >> character that could not be used in the conversion.

    >
    > do you know maybe on a function C that i can use,its just need o check
    > if its a float value or non float number


    Oh, I see. Well, in that case I suggest you capture the data as a string,
    use strtod to convert to floating-point form, and use the ability strtod
    gives you to identify the position of the first character that could not be
    used in the conversion.

    --
    Richard Heathfield
    "Usenet is a strange place" - dmr 29/7/1999
    http://www.cpax.org.uk
    email: rjh at above domain (but drop the www, obviously)
     
    Richard Heathfield, Aug 5, 2006
    #4
  5. On 2006-08-05, Richard Heathfield <> wrote:
    > said:
    >> Richard Heathfield said:
    >>> said:
    >>>
    >>> > hi all,
    >>> > i building an equition with float type now,what function should i use
    >>> > in order to identify is someone enter a char value by mistake?
    >>>
    >>> Capture the input as a string.
    >>> Use strtod to convert to floating-point form.
    >>> Use the ability strtod gives you to identify the position of the first
    >>> character that could not be used in the conversion.

    >>
    >> do you know maybe on a function C that i can use,its just need o check
    >> if its a float value or non float number

    >
    > Oh, I see. Well, in that case I suggest you capture the data as a string,
    > use strtod to convert to floating-point form, and use the ability strtod
    > gives you to identify the position of the first character that could not be
    > used in the conversion.
    >


    Another way would be to capture the data as a string, use strtol to convert
    to integer-form, and use the ability strtol gives you to identify the
    position of the first character that could not be used in the conversion.

    --
    Andrew Poelstra <http://www.wpsoftware.net/projects>
    To reach me by email, use `apoelstra' at the above domain.
    "Do BOTH ends of the cable need to be plugged in?" -Anon.
     
    Andrew Poelstra, Aug 5, 2006
    #5
  6. Andrew Poelstra said:

    <snip>
    >
    > Another way would be to capture the data as a string, use strtol to
    > convert to integer-form, and use the ability strtol gives you to identify
    > the position of the first character that could not be used in the
    > conversion.


    That wouldn't help him a lot with floating-point, which he specifically
    mentioned he was using.

    --
    Richard Heathfield
    "Usenet is a strange place" - dmr 29/7/1999
    http://www.cpax.org.uk
    email: rjh at above domain (but drop the www, obviously)
     
    Richard Heathfield, Aug 5, 2006
    #6
  7. On 2006-08-05, Richard Heathfield <> wrote:
    > Andrew Poelstra said:
    >
    ><snip>
    >>
    >> Another way would be to capture the data as a string, use strtol to
    >> convert to integer-form, and use the ability strtol gives you to identify
    >> the position of the first character that could not be used in the
    >> conversion.

    >
    > That wouldn't help him a lot with floating-point, which he specifically
    > mentioned he was using.
    >


    Oh; by `char' did he mean character? I was thinking tiny integer, and
    figured that if strtol() said that a '.' was the first invalid character
    (and the character after that was a digit), he could consider it floating
    point.

    --
    Andrew Poelstra <http://www.wpsoftware.net/projects>
    To reach me by email, use `apoelstra' at the above domain.
    "Do BOTH ends of the cable need to be plugged in?" -Anon.
     
    Andrew Poelstra, Aug 5, 2006
    #7
  8. Andrew Poelstra said:

    > On 2006-08-05, Richard Heathfield <> wrote:
    >> Andrew Poelstra said:
    >>
    >><snip>
    >>>
    >>> Another way would be to capture the data as a string, use strtol to
    >>> convert to integer-form, and use the ability strtol gives you to
    >>> identify the position of the first character that could not be used in
    >>> the conversion.

    >>
    >> That wouldn't help him a lot with floating-point, which he specifically
    >> mentioned he was using.
    >>

    >
    > Oh; by `char' did he mean character?


    I think what he meant was that he was expecting:

    3.14159

    and was worried that he might get

    THREE POINT ONE FOUR ONE FIVE NINE

    or

    ELEPHANT

    instead.

    > I was thinking tiny integer, and
    > figured that if strtol() said that a '.' was the first invalid character
    > (and the character after that was a digit), he could consider it floating
    > point.


    When you are asking the user for, say, the width of a square, it is not
    unreasonable for the user to be able to enter a value without a decimal
    point, or with one, and still expect the program to be able to calculate
    that square's area. So the presence or absence of '.' is not a good test.

    --
    Richard Heathfield
    "Usenet is a strange place" - dmr 29/7/1999
    http://www.cpax.org.uk
    email: rjh at above domain (but drop the www, obviously)
     
    Richard Heathfield, Aug 5, 2006
    #8
  9. Guest

    wrote:
    > hi all,
    > i building an equition with float type now,what function should i use
    > in order to identify is someone enter a char value by mistake?


    You should be able to write a simple fuzzer that hits your arguments
    to make sure your program is properly checking inputs. Actually,
    I would take this time to write a fuzzer to just learn how they are
    built.
    Ruby or Perl would both be good command line choices.

    Here's an implementation of what the guys above are talking about:

    #include <stdio.h>
    #include <stdlib.h>

    int
    main(int argc, char **argv) {

    char **endptr;
    double output;

    if(argc < 2) {
    (void)printf("usage: strtof <digit>\n");
    exit(1);
    }

    output = strtod(argv[1], endptr);
    if (output == 0) {
    perror("string to double conversion failed");
    exit(1);
    }
    (void)printf("%f\n", output);
    exit(0);
    }
     
    , Aug 6, 2006
    #9
  10. said:

    <snip>
    >
    > Here's an implementation of what the guys above are talking about:
    >
    > #include <stdio.h>
    > #include <stdlib.h>
    >
    > int
    > main(int argc, char **argv) {
    >
    > char **endptr;


    Huh?

    <snip>

    > output = strtod(argv[1], endptr);


    Wrong.

    --
    Richard Heathfield
    "Usenet is a strange place" - dmr 29/7/1999
    http://www.cpax.org.uk
    email: rjh at above domain (but drop the www, obviously)
     
    Richard Heathfield, Aug 6, 2006
    #10
  11. Guest


    > > output = strtod(argv[1], endptr);

    >
    > Wrong.


    Why is this wrong? If all he is doing is testing to see whether the
    input is valid, then all he needs to know is if strtod can convert. If
    you fuzz the input with anything but numbers, the conversion will fail.

    $ gdb strtof
    GNU gdb 6.3
    Copyright 2004 Free Software Foundation, Inc.
    GDB is free software, covered by the GNU General Public License, and
    you are
    welcome to change it and/or distribute copies of it under certain
    conditions.
    Type "show copying" to see the conditions.
    There is absolutely no warranty for GDB. Type "show warranty" for
    details.
    This GDB was configured as "amd64-unknown-openbsd4.0"...
    (gdb) break main
    Breakpoint 1 at 0x40089f: file strtof.c, line 12.
    (gdb) run aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
    Starting program: /anime/strtof
    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

    Breakpoint 1, main (argc=2, argv=0x7f7ffffd4870) at strtof.c:12
    12 if(argc < 2) {
    (gdb) step
    17 output = strtod(argv[1], endptr);
    (gdb) info locals
    endptr = (char **) 0x7f7ffffd4860
    output = 6.9261942529720109e-310
    (gdb) until
    18 if (output == 0) {
    (gdb) info locals
    endptr = (char **) 0x7f7ffffd4860
    output = 0
    (gdb) p *endptr
    $1 = 0x7f7ffffd4d26 'a' <repeats 46 times>

    Please explain why this is a bad approach.
     
    , Aug 6, 2006
    #11
  12. said:

    >> > output = strtod(argv[1], endptr);

    >>
    >> Wrong.

    >
    > Why is this wrong?


    Because you're evaluating an indeterminate value - to wit, endptr.

    --
    Richard Heathfield
    "Usenet is a strange place" - dmr 29/7/1999
    http://www.cpax.org.uk
    email: rjh at above domain (but drop the www, obviously)
     
    Richard Heathfield, Aug 6, 2006
    #12
  13. Bill Pursell Guest

    Richard Heathfield wrote:
    > said:
    >
    > >> > output = strtod(argv[1], endptr);
    > >>
    > >> Wrong.

    > >
    > > Why is this wrong?

    >
    > Because you're evaluating an indeterminate value - to wit, endptr.


    It should be:
    output = strtod(argv[1], &endptr), and endptr
    should be declared as char *.

    But I don't understand why you will consider
    it a failure if the user enters "0"--that seems like a valid
    input. It might be more appropriate to check
    for *endptr == argv[1], or perhaps **endptr == '\0'.
     
    Bill Pursell, Aug 6, 2006
    #13
  14. Guest

    Bill Pursell wrote:

    > It might be more appropriate to check
    > for *endptr == argv[1], or perhaps **endptr == '\0'.


    Now, I do agree with that approach. That's a far better way
    to check the value. You would also check for ERANGE
    and limits within HUGE_VAL.

    This approach is documented in strtol(3).

    Brian
     
    , Aug 6, 2006
    #14
  15. Bill Pursell said:

    <snip>

    > It might be more appropriate to check
    > for *endptr == argv[1], or perhaps **endptr == '\0'.


    No, because once you define endptr properly as char *, *endptr will be a
    char, not a char *.

    But you're right in that finding out where the conversion stopped happening
    is your first step in validating the data.

    --
    Richard Heathfield
    "Usenet is a strange place" - dmr 29/7/1999
    http://www.cpax.org.uk
    email: rjh at above domain (but drop the www, obviously)
     
    Richard Heathfield, Aug 6, 2006
    #15
  16. On Sat, 05 Aug 2006 17:10:51 +0000, Richard Heathfield wrote:

    > Andrew Poelstra said:
    >
    > <snip>
    >>
    >> Another way would be to capture the data as a string, use strtol to
    >> convert to integer-form, and use the ability strtol gives you to
    >> identify the position of the first character that could not be used in
    >> the conversion.

    >
    > That wouldn't help him a lot with floating-point, which he specifically
    > mentioned he was using.



    Using strtol is possible, but it must be used thoughtfully:


    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <limits.h>

    /*
    * Validates numbers of the form n{0,6}[.[n{0,6}]]
    */

    float
    validate_float(const char *data)
    {
    long whole, fractional;
    static long powers [] = {1, 10, 100, 1000, 10000, 100000, 1000000};
    #define MAXFRACT (sizeof(powers)/sizeof(powers[0])-1)
    char *decptr, *endptr;
    int decimals;
    float number;

    /* Nip out empty strings and naked decimal. NOTE: Adjust to locale. */
    if (!strlen(data) || !strcmp(data, ".")) return -1;

    /* Get whole part */
    whole = strtol(data, &decptr, 10);

    /* Verify that whole part is in range */
    if (whole < 0 || whole == LONG_MAX) return -1;

    /* Return whole number here. */
    if (!*decptr) return whole;

    /* Demand decimal point. NOTE: Adjust to locale. */
    if (*decptr != '.') return -1;

    /* Get fractional part if any */
    fractional = strtol(decptr+1, &endptr, 10);

    /* Check fractional part for bad characters */
    if (*endptr) return -1;

    /* Verify that fractional part is in range. */
    if (fractional < 0 || endptr - (decptr+1) > MAXFRACT) return -1;

    /* Make fractional adjustment */
    decimals = strlen(decptr+1);
    if (decimals > MAXFRACT) decimals = MAXFRACT;

    /* Calculate number */
    number = whole + (double)fractional/powers[decimals];
    return number;
    }

    int
    main(int argc, char *argv[])
    {
    double converted;


    #define DOSTRING(STRING) do { \
    converted = validate_float(STRING); \
    if (converted < 0) printf("invalid '"STRING"'\n"); \
    else printf(STRING" -> %f\n", converted); \
    } while(0)


    /* Invalid */
    DOSTRING("");
    DOSTRING(".");
    DOSTRING("0.x");
    DOSTRING("0.0x");
    DOSTRING("123456.1234567");
    DOSTRING("-1");

    /* Valid */
    DOSTRING("0");
    DOSTRING(".0");
    DOSTRING("0.");
    DOSTRING("0.0");
    DOSTRING("1");
    DOSTRING(".1");
    DOSTRING("1.");
    DOSTRING("1.1");
    DOSTRING("123456");
    DOSTRING(".123456");
    DOSTRING("1.123456");

    return 0;
    }


    invalid ''
    invalid '.'
    invalid '0.x'
    invalid '0.0x'
    invalid '123456.1234567'
    invalid '-1'
    0 -> 0.000000
    ..0 -> 0.000000
    0. -> 0.000000
    0.0 -> 0.000000
    1 -> 1.000000
    ..1 -> 0.100000
    1. -> 1.000000
    1.1 -> 1.100000
    123456 -> 123456.000000
    ..123456 -> 0.123456
    1.123456 -> 1.123456


    Gratuitously,

    Martin
    --
    Martin Golding | You don't need computers to screw things up,
    DoD #236 BMWMOA #55952 | but it does make the work go faster. (Ed)
    Vancouver, WA
     
    Martin Golding, Aug 6, 2006
    #16
  17. Martin Golding said:
    > On Sat, 05 Aug 2006 17:10:51 +0000, Richard Heathfield wrote:
    >> Andrew Poelstra said:
    >> <snip>
    >>> Another way would be to capture the data as a string, use strtol to
    >>> convert to integer-form, and use the ability strtol gives you to
    >>> identify the position of the first character that could not be used in
    >>> the conversion.

    >>
    >> That wouldn't help him a lot with floating-point, which he specifically
    >> mentioned he was using.

    >
    > Using strtol is possible, but it must be used thoughtfully:


    <snip>

    > invalid '123456.1234567'
    > invalid '-1'


    On what grounds do you reject these? And why does your code reject 1e-6 ?

    --
    Richard Heathfield
    "Usenet is a strange place" - dmr 29/7/1999
    http://www.cpax.org.uk
    email: rjh at above domain (but drop the www, obviously)
     
    Richard Heathfield, Aug 6, 2006
    #17
  18. Guest

    Bill Pursell wrote:

    >
    > But I don't understand why you will consider
    > it a failure if the user enters "0"--that seems like a valid
    > input. It might be more appropriate to check
    > for *endptr == argv[1], or perhaps **endptr == '\0'.


    I changed endptr to a char *. I don't think it really matters as I
    would be de-referencing that variable anyway. I would like to hear why
    you prefer that approach. Is it code readability?

    Here's a working version:

    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <math.h>

    int
    main(int argc, char **argv) {

    char *endptr;
    char *input;
    double output;

    input = argv[1];
    if(argc < 2) {
    (void)printf("usage: strtof <digit>\n");
    exit(1);
    }

    output = strtod(input, &endptr);
    if (input[0] == '\0' || *endptr != '\0') {
    perror("string to double conversion failed");
    exit(1);
    }
    (void)printf("%f\n", (float)output);
    exit(0);
    }

    Here are some debugging results:

    $ gdb strtof
    GNU gdb 6.3
    Copyright 2004 Free Software Foundation, Inc.
    GDB is free software, covered by the GNU General Public License, and
    you are
    welcome to change it and/or distribute copies of it under certain
    conditions.
    Type "show copying" to see the conditions.
    There is absolutely no warranty for GDB. Type "show warranty" for
    details.
    This GDB was configured as "amd64-unknown-openbsd4.0"...
    (gdb) break main
    Breakpoint 1 at 0x40089f: file strtof.c, line 13.
    (gdb) run 0a1b2c
    Starting program: /anime/strtof 0a1b2c

    Breakpoint 1, main (argc=2, argv=0x7f7ffffec080) at strtof.c:13
    13 input = argv[1];
    (gdb) info locals
    endptr = 0x7f7ffffec070 ""
    input = 0x7f7ffffec528 "/anime/strtof"
    output = 4.1461860541929491e-317
    (gdb) step
    14 if(argc < 2) {
    (gdb) step
    19 output = strtod(input, &endptr);
    (gdb) until
    20 if (input[0] == '\0' || *endptr != '\0') {
    (gdb) info locals
    endptr = 0x7f7ffffec537 "a1b2c"
    input = 0x7f7ffffec536 "0a1b2c"
    output = 0
    (gdb) p *endptr
    $1 = 97 'a'
    (gdb) p input[0]
    $2 = 48 '0'
    (gdb) step
    21 perror("string to double conversion failed");
    (gdb)

    and here are some results to show that I can now enter a 0 as a valid
    entry:

    $ gdb strtof
    GNU gdb 6.3
    Copyright 2004 Free Software Foundation, Inc.
    GDB is free software, covered by the GNU General Public License, and
    you are
    welcome to change it and/or distribute copies of it under certain
    conditions.
    Type "show copying" to see the conditions.
    There is absolutely no warranty for GDB. Type "show warranty" for
    details.
    This GDB was configured as "amd64-unknown-openbsd4.0"...
    (gdb) break main
    Breakpoint 1 at 0x40089f: file strtof.c, line 13.
    (gdb) run 0
    Starting program: /anime/strtof 0

    Breakpoint 1, main (argc=2, argv=0x7f7fffff5d18) at strtof.c:13
    13 input = argv[1];
    (gdb) info locals
    endptr = 0x7f7fffff5d00 ""
    input = 0x7f7fffff61c0 "/anime/strtof"
    output = 4.1461860541929491e-317
    (gdb) step
    14 if(argc < 2) {
    (gdb) step
    19 output = strtod(input, &endptr);
    (gdb) until
    20 if (input[0] == '\0' || *endptr != '\0') {
    (gdb) info locals
    endptr = 0x7f7fffff61cf ""
    input = 0x7f7fffff61ce "0"
    output = 0
    (gdb) p input[0]
    $1 = 48 '0'
    (gdb) p *endptr
    $2 = 0 '\0'
    (gdb) step
    24 (void)printf("%f\n", (float)output);
    (gdb) info locals
    endptr = 0x7f7fffff61cf ""
    input = 0x7f7fffff61ce "0"
    output = 0
    (gdb)
     
    , Aug 6, 2006
    #18
  19. said:

    >
    > Bill Pursell wrote:
    >
    >>
    >> But I don't understand why you will consider
    >> it a failure if the user enters "0"--that seems like a valid
    >> input. It might be more appropriate to check
    >> for *endptr == argv[1], or perhaps **endptr == '\0'.

    >
    > I changed endptr to a char *. I don't think it really matters as I
    > would be de-referencing that variable anyway.


    It matters very much, because strtod expects the /address/ of an object that
    has type char *. You do it like this:

    char *endptr;
    double d = strtod(s, &endptr);

    if(endptr == s)
    {
    no characters were converted, so you know that the input was not a valid
    text representation of a double.
    }

    --
    Richard Heathfield
    "Usenet is a strange place" - dmr 29/7/1999
    http://www.cpax.org.uk
    email: rjh at above domain (but drop the www, obviously)
     
    Richard Heathfield, Aug 6, 2006
    #19
  20. Guest

    Richard Heathfield wrote:

    > It matters very much, because strtod expects the /address/ of an object that
    > has type char *. You do it like this:
    >
    > char *endptr;
    > double d = strtod(s, &endptr);
    >



    I follow you. However, strtod expects a parameter of type char **. I
    can pass the address of a pointer, or I can just pass a variable of
    type char **. Either way, the only thing that changes is how I
    dereference the pointer when the function returns.

    Here's the same program with endptr declared as char **:

    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <math.h>

    int
    main(int argc, char **argv) {

    char **endptr;
    char *input;
    double output;

    input = argv[1];

    if(argc < 2) {
    (void)printf("usage: strtof <digit>\n");
    exit(1);
    }


    output = strtod(input, endptr);
    if (**endptr != '\0') { /* dereference of pointer changes */
    perror("string to double conversion failed");
    exit(1);
    }
    (void)printf("%f\n", (float)output);
    exit(0);
    }

    The above passes gcc -Wall -pedantic without warning.

    > if(endptr == s)
    > {
    > no characters were converted, so you know that the input was not a valid
    > text representation of a double.
    > }


    Now, if I entered:

    12......222.....222

    strtod() would convert the first two digits in your example and dump
    them to output. If you really wanted to be particular, you could also
    check for NOP sleds as well. Of course, this would reduce the
    portability of your code.
     
    , Aug 6, 2006
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. bd
    Replies:
    0
    Views:
    651
  2. lovecreatesbeauty
    Replies:
    1
    Views:
    1,099
    Ian Collins
    May 9, 2006
  3. Replies:
    7
    Views:
    394
    red floyd
    Dec 9, 2005
  4. Carsten Fuchs
    Replies:
    45
    Views:
    1,608
    James Kanze
    Oct 8, 2009
  5. jlc488
    Replies:
    4
    Views:
    2,405
    jlc488
    Dec 14, 2009
Loading...

Share This Page