indentify if argument is char or float

Y

yaniv.dg

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?
 
R

Richard Heathfield

(e-mail address removed) 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.
 
Y

yaniv.dg

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 כתב:
 
R

Richard Heathfield

(e-mail address removed) said:
Richard Heathfield said:

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.
 
A

Andrew Poelstra

(e-mail address removed) said:

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.
 
R

Richard Heathfield

Andrew Poelstra said:

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.
 
A

Andrew Poelstra

Andrew Poelstra said:



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.
 
R

Richard Heathfield

Andrew Poelstra said:
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.
 
B

bwaichu

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);
}
 
R

Richard Heathfield

(e-mail address removed) said:

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?

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

Wrong.
 
B

bwaichu

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.
 
B

Bill Pursell

Richard said:
(e-mail address removed) 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'.
 
B

bwaichu

Bill said:
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
 
R

Richard Heathfield

Bill Pursell said:

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.
 
M

Martin Golding

Andrew Poelstra said:



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
 
B

bwaichu

Bill said:
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)
 
R

Richard Heathfield

(e-mail address removed) said:
Bill said:
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.
}
 
B

bwaichu

Richard said:
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.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,432
Messages
2,571,682
Members
48,796
Latest member
Greg L.

Latest Threads

Top