verify float number

K

Keith Thompson

Xancatal said:
Thanks Neil, how do you think I could convert it? I tried this and it
failed during compile time:


float myVar = 0.0;
...
...
...

scanf ("%c", &otherVar);

The "%c" format expects a pointer to the first element of a character
array. With no length specified, the length defaults to one. A
single character object, in this context, acts like an array of length
one. You didn't show us the declaration of otherVar, but for this to
work, it should be of type char. So this is a (somewhat convoluted)
way to read a single character.
myVar = &otherVar;

It looks as although I tried to assign the value of this string to the
float, it fails compilation. It says it can not assign value.

&otherVar is the address of your char variable, so it's of type char*
(pointer-to-char). You can't assign a char* to a float; it doesn't
even make sense to try.

Part of the problem is that there are multiple ways to do what you're
trying to do.

One of the most general ways to handle text input is:

Use fgets() to read a whole line at a time into a string. Note
that the resulting string will usually contain the '\n' character
that terminated the line. (If the input line is longer than your
buffer, or if you hit end-of-file or an error, things can get more
complicated.)

Use sscanf(), or perhaps some more specific function like
strtol(), to extract the data from the string.

You *can* use scanf(), but it combines reading input and parsing it
("parsing" means analyzing data in a sequence of characters, either a
string or an input file), and it doesn't respond well to errors.
Using fgets() and sscanf() separately, if sscanf() fails, you can
easily discard the entire line, or try again with a different format.
scanf() or fscanf() is less flexible; if there's an error, it's
already consumed some amount of input.
 
B

Ben Pfaff

Nick Keighley said:
no no no! This is your fundamental misunderstanding. Take a deep
breath. Most of the checking you want to do ***is done by
scanf()***.

scanf is flawed to an extent that it is barely useful, because it
yields undefined behavior if out-of-range data is input. See C99
7.19.6.2p10:

...if the result of the conversion cannot be represented
in the object, the behavior is undefined.

I am amazed that the committee did not fix this in C99.
 
C

Chris Torek

... Is only a matter of letting a user enter a dollar amount, and
letting the program determine if it is a dollar amount to do some
calculation, or otherwise prompt an error.

If you really want to scan "dollar amount"s, you might find some
code I have lying around here useful.

The comp.lang.c folks can do their usual work on this to improve
it. :)

(I have removed some parts that are not relevant, but left others
in since I have little time to make this posting. If I had more
time I could post a shorter article. Some additional editing will
be required to make this compile.)

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

static const char whitespace[] = " \t";

static void read_line(char *, int, char *);
static int getdouble(char **, double *, int);

int
read_input(char *fname, FILE *in)
{
int c;
char *p;
int lineno = 0;
char line[BUFSIZ];

while ((p = fgets(line, sizeof line, in)) != NULL) {
lineno++;
p = strchr(p, '\n');
if (p == NULL) {
/* not the best, but ... */
printf("%s:%d: overlong input line truncated\n",
fname, lineno);
while ((c = fgetc(in)) != EOF && c != '\n')
continue;
} else
*p = '\0';
read_line(fname, lineno, line);
}
return 0; /* XXX should return error count? */
}

/*
* Break the line into tokens, then act on them.
* This is a pretty cheesy lexer/parser but what the heck.
* We use the date parser to make sure the first (assumed to be date)
* field is sensible.
*/
static void
read_line(char *fname, int lineno, char *line)
{
char *p, *ep;
unsigned char c;
[snippage]
int rv;
double n2;

p = line;
p += strspn(p, whitespace);
if (*p == 0 || *p == '#')
return; /* ignore blank/comment lines */
ep = p;
[snippage -- code that extracted date, record type, etc]
if (getdouble(&ep, &n2, 1)) {
printf("%s:%d: malformed %s -- bad $ value\n",
fname, lineno, "line");
return;
}
[snippage]
}

#define COMMA ','
/*
* Rather stupid little algorithm to clean up "money style" numbers --
* replaces removed commas with trailing whitespace (ugh, but what
* the heck).
*/
static int
cleanmoney(char *st, char **epp)
{
size_t m;
char *t, *en;
enum { MAYBE, YES, NO } state;

en = *epp;
t = memchr(st, '.', en - st);
if (t != NULL) {
if (memchr(t + 1, COMMA, en - t - 1) != NULL)
return -1;
/* ok, no commas past decimal point */
} else
t = en;
m = t - st;
state = MAYBE;
while (m > 3) {
/*
* there are at least 4 (more) digits; t points past
* the last of a group of 3 (incl eg to decpt or '\0'):
*
* "1,234.50"
* t
*
* Back it up 4 to point at the "possible comma".
* Reject if the 3 digits are a comma; then check/set
* state for comma-eating.
*/
t -= 4;
if (t[1] == COMMA || t[2] == COMMA || t[3] == COMMA)
return -1;
if (state == MAYBE)
state = t[0] == COMMA ? YES : NO;
if (t[0] == COMMA && state != YES)
return -1;
if (t[0] != COMMA && state == YES)
return -1; /* must be consistent */
if (state == YES) {
/* remove this comma */
memmove(t, t + 1, en - t - 1);
*--en = ' '; /* whack in a blank and shorten */
}
m -= 3;
}
*epp = en;
return 0;
}

/*
* Break off whitespace-separated token and turn it into a double
* via strtod. Allow "dollar" values (prefix $, embedded commas)
* if directed.
*
* Return 0 if that seemed to work.
*/
static int
getdouble(char **pp, double *result, int dollars)
{
char *cp, *ep, *ep2;
unsigned char c;

cp = *pp;
cp += strspn(cp, whitespace);
c = *cp;
if (dollars && c == '$')
c = *++cp;
if (!isdigit(c))
return -1;
ep = cp + strcspn(cp, whitespace);
if (dollars && cleanmoney(cp, &ep))
return -1;
*result = strtod(cp, &ep2);
if (ep != ep2)
return -1;
*pp = ep;
return 0;
}
 
B

Bill Medland

Bill said:
Ah. Now we're getting somewhere.
In that case I would definitely use fgets(mystr,sizeof(mystr), stdin)),
look carefully at the string and parse out the leading and trailing digits
(look up strchr).
If it's dollar amounts I probably wouldn't use float either; I would use
an integer and count in cents.
Something like this.
(You'll still have to tidy it up but hopefully it will give you enough to
get you going.)
/*
* Read in an amount in a very restrictive format
*/
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
int main (int argc, char **argv)
{
/* I'm not proud of it but it should give Xancatal some pointers */
char buffer[20];
long cents = -1;
fprintf(stdout, "Enter an amount\n");
if (fgets(buffer, sizeof(buffer), stdin))
{
const char *p;
/* Skip over leading spaces, tabs etc. */
for (p = buffer; isspace(*p); p++)
;
if (isdigit(*p))
{
const char *first_digit;
const char *period;
first_digit = p;
for (; isdigit(*p); p++)
;
if (*p == '.')
{
period = p;
p++;
for (;isdigit(*p); p++)
;
if (*p == '\0')
{
fprintf(stderr, "Oh dear, the buffer was too small\n");
}
else if (*p != '\n')
{
fprintf(stderr, "Invalid additional input %s\n", p);
}
else if (p-period != 3)
{
fprintf(stderr, "Expected two figures after the
point\n");
}
else
{
char *end1;
char *end2;
cents = 100 * strtol(first_digit, &end1, 10) +
strtol(period+1, &end2, 10);
assert(end1 == period);
assert(end2 == p);
assert(cents >= 0);
}
}
else if (*p == '\0')
{
fprintf(stderr, "Oh dear, the buffer was too small\n");
}
else if (*p != '\n')
{
fprintf(stderr, "Invalid additional input %s\n", p);
}
else
{
char *end;
cents = 100 * strtol(first_digit, &end, 10);
assert(end == p);
assert(cents >= 0);
}
if (cents >= 0)
fprintf(stdout, "You entered %ld cents\n", cents);
}
else if (*p == '\0')
{
fprintf(stderr, "Oh dear, the buffer was too small\n");
}
else
{
fprintf(stderr, "Invalid input %s\n", p);
}
}
else if (ferror(stdin))
perror("Error reading input");
else
{
assert(feof(stdin));
fprintf(stderr, "No input\n");
}
return 0;
}
 
X

Xancatal

/*
* Read in an amount in a very restrictive format
*/
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
int main (int argc, char **argv)
{
/* I'm not proud of it but it should give Xancatal some pointers */

Bill,

Thanks for the code. I was unable to compile it though. I get this
error:

C:\Documents and Settings\Administrator\My
Documents\dollarProject\registersNevada\test.c: line 16: incompatible
types in initializer
'long cents = -1'
aborting compile

Any clues?
 
A

av

Hey everybody. I need help on this one. I need to verify that a number
entered by a user is not either a negative number (-100.00), or an
alphabet (a, b, c, X, Y) as well as other number other than positive
integers or a decimal point. For example:

Enter amount:

and was capturing the float varialbe as in:

scanf ("%f", &myVar)

I was using scanf to capture the data, but I'm having a hard time
verifying this float with isdigit or isalpha. Any ideas would be
greatly appreciated.

Thanks


you have just to write the right getl_() for get the line
and sscanf_m like sscanf (that return error if oveflow too)

// se ritorna 1=> valore preso in val
// altrimenti valore non preso
int
get_double(double* val, double min, double max, FILE_m* pf)
{char a[512];
int k, i=0, cf1;
la0:;
k=getl_m(a, 512, pf); cf1=cf();
if(k==0){if(cf1==1)
{laa:; printf("\n"); return 0;}
printf("Errore linea troppo lunga"); goto la2;
}
if( (k=sscanf_m(a, "%f", val))!= 1 )
if(cf1==1) goto laa;
if(k!=1||*val<min||*val>max)
{la1:;
printf("Errore o importo fuori range");
la2:;
if(++i>=3) goto laa;
printf(": Riprova > ");
goto la0;
}
return 1;
}

usage:
double val;
printf("Inserisci il valore > ");
if( get_double(&val, 0.0, 12.0, stdin) )
return 0;
 
N

Nick Keighley

av said:
On 23 Oct 2006 08:56:42 -0700, Xancatal wrote:
Hey everybody. I need help on this one. I need to verify that a number
entered by a user is not either a negative number (-100.00), or an
alphabet (a, b, c, X, Y) as well as other number other than positive
integers or a decimal point. For example:

Enter amount:

and was capturing the float varialbe as in:

scanf ("%f", &myVar)

I was using scanf to capture the data, but I'm having a hard time
verifying this float with isdigit or isalpha. Any ideas would be
greatly appreciated.

you have just to write the right getl_() for get the line
and sscanf_m like sscanf (that return error if oveflow too)

// se ritorna 1=> valore preso in val
// altrimenti valore non preso
int
get_double(double* val, double min, double max, FILE_m* pf)
{char a[512];
int k, i=0, cf1;
la0:;
k=getl_m(a, 512, pf); cf1=cf();
if(k==0){if(cf1==1)
{laa:; printf("\n"); return 0;}
printf("Errore linea troppo lunga"); goto la2;
}
if( (k=sscanf_m(a, "%f", val))!= 1 )
if(cf1==1) goto laa;
if(k!=1||*val<min||*val>max)
{la1:;
printf("Errore o importo fuori range");
la2:;
if(++i>=3) goto laa;
printf(": Riprova > ");
goto la0;
}
return 1;
}

usage:
double val;
printf("Inserisci il valore > ");
if( get_double(&val, 0.0, 12.0, stdin) )
return 0;

congratulations. In only 20 lines of code you've used more gotos
than I've used in my entire career.


--
Nick Keighley

Soon I moved onto the hard stuff - computed goto. Lord, that was cool.
I
could jump to any of a dozen places with one line of code. Talk about
POWER!!
Richard Heathfield "goto: Just Say No."
 
B

Bill Medland

Xancatal said:
Bill,

Thanks for the code. I was unable to compile it though. I get this
error:

C:\Documents and Settings\Administrator\My
Documents\dollarProject\registersNevada\test.c: line 16: incompatible
types in initializer
'long cents = -1'
aborting compile

Any clues?
Yes.
Either use int rather than long or use -1L for the constant.
 
K

Keith Thompson

Bill Medland said:
Yes.
Either use int rather than long or use -1L for the constant.

No.

The declaration

long cents = -1;

is perfectly legal. The constant 1 has type int. Applying the unary
"-" operator yields a result of type int, with the obvious value. The
expression is used to initialize an object of type long, so it's
implicitly converted from int to long.

A diagnostic (warning message) is allowed, as it is for literally
anything, but it would be silly in this context, and I don't know of
any compiler that would issue a warning here. A compiler that rejects
the declaration either is badly broken, or is not a C compiler.

Something else is going on here, and changing "-1" to "-1L" is not
going to fix it (though it might conceivably work around it).

I compiled the code myself, and it compiled with no errors or
warnings. Perhaps Xancatal is doing something odd (using something
other than a C compiler, or compiling something other than the posted
source), but I have no idea what. If Xancatal can give us some more
details, we might be able to help.
 
X

Xancatal

I compiled the code myself, and it compiled with no errors or
warnings. Perhaps Xancatal is doing something odd (using something
other than a C compiler, or compiling something other than the posted
source), but I have no idea what. If Xancatal can give us some more
details, we might be able to help.

Keith,

Thanks for your insight. I'm using a toy compiler as you can see. I
will try this out with a better compiler. I have Sun Studio 11 in
another system, but is quite complicated to compile and run simple
programs, for it develops more robust software. Does anyone know of a
better C compiler (perhaps a freebee?)

As soon as I get the compiler, I will re-try this code. I get all kinds
of weird errors when I try to compile this code as it is. :(
 
I

Ian Collins

Xancatal said:
Keith,

Thanks for your insight. I'm using a toy compiler as you can see. I
will try this out with a better compiler. I have Sun Studio 11 in
another system, but is quite complicated to compile and run simple
programs, for it develops more robust software

What's complicated about cc filename.c?
 
R

Richard Heathfield

Xancatal said:
I'm using the GUI Ian.

I sympathise. GUIs do make computers much harder to use, don't they?
Fortunately, they are not always compulsory.
 
C

CBFalconer

Richard said:
Xancatal said:

I sympathise. GUIs do make computers much harder to use, don't
they? Fortunately, they are not always compulsory.

Well put. I prefer to tell the machine what to do rather than to
have it give me permission to do limited things.
 
J

J. J. Farrell

Xancatal said:
I'm using the GUI Ian.

If you find the GUI hard to use, why on earth do you use it? Why not
just follow Ian's comprehensive instructions?
 
J

John Bode

Xancatal said:
Hey everybody. I need help on this one. I need to verify that a number
entered by a user is not either a negative number (-100.00), or an
alphabet (a, b, c, X, Y) as well as other number other than positive
integers or a decimal point. For example:

Enter amount:

and was capturing the float varialbe as in:

scanf ("%f", &myVar)

I was using scanf to capture the data, but I'm having a hard time
verifying this float with isdigit or isalpha. Any ideas would be
greatly appreciated.

Thanks

So basically, you need to verify that an input is a properly formed,
positive floating point value.

The easy way would be to read the input as a string with fgets(), then
use strtod() to both convert it to a double value and to verify that
the input is valid, and then test that it's positive:

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

#define MAXBUF ... // large enough to hold largest input

int main(void)
{
char inBuf[MAXBUF+1];
char *endInputChar;
double inValue;

printf("Gimme a number: ");
fflush(stdout);

if (fgets(inBuf, sizeof inBuf, stdin) != NULL)
{
inValue = strtod(inBuf, &endInputChar);
/**
* endInputChar points to the first character in the inBuf
* string that is *not* converted to a floating-point
* value by strtod(). If this character is whitespace or 0,
* the string is a valid float. If the character is anything
* else, then the string is not a valid float.
*/
if (isspace(*endInputChar) || *endInputChar != 0)
{
if (inValue > 0.0)
{
printf("\"%s\" is a valid, positive floating-point
value\n", inBuf)
}
else
{
printf("\"%s\" is not a positive-valued floating-point
value:\n",
inBuf, );
}
}
else
{
/**
* strtod() encountered an invalid character, which
* is pointed to by endInputChar.
*/
printf("\"%s\" is not a valid floating-point number: bad
character '%c'\n",
inBuf, *endInputChar);
}
}
else
{
printf("Error occurred while reading standard input\n");
}

return 0;
}

The slightly harder way is to write a simple lexer that scans the
string one character at a time and changes state based on the
character. It's a bit more work (enough so that I won't provide an
example at this time; maybe later when I'm not supposed to be getting
real work done), but it allows you to constrain the input to your
specific needs (for example, you may not want to allow input such as
1.0e3, which the above code will convert without complaint).
 
D

Dave Thompson

On 25 Oct 2006 01:01:24 -0700, "Nick Keighley"
Soon I moved onto the hard stuff - computed goto. Lord, that was cool.
I
could jump to any of a dozen places with one line of code. Talk about
POWER!!
Richard Heathfield "goto: Just Say No."

Maybe a dozen places, or even more, but only as many as you can write
out in one statement, and anyone can easily find the list in a single
place, even a reviewer or maintainer or other such troublemaker.

Now, _assigned_ goto, that's the ticket. And usually with only little
effort you can trash the bits and go to places that aren't labels, nor
even statements, and if you're lucky nor even instructions.

<G!>

- David.Thompson1 at worldnet.att.net
 
C

Chris Torek

On 25 Oct 2006 01:01:24 -0700, "Nick Keighley"
Maybe a dozen places, or even more, but only as many as you can write
out in one statement, and anyone can easily find the list in a single
place, even a reviewer or maintainer or other such troublemaker.

Now, _assigned_ goto, that's the ticket. And usually with only little
effort you can trash the bits and go to places that aren't labels, nor
even statements, and if you're lucky nor even instructions.

<G!>

Alas, C lacks assigned goto (although GNUC provides it, with
the unary && operator to take the address of labels).

For similar destructive power, there is always COBOL with its ALTER
statement.
 
R

Richard Heathfield

Chris Torek said:

Alas, C lacks assigned goto (although GNUC provides it, with
the unary && operator to take the address of labels).

setjmp comes pretty close, doesn't it?
For similar destructive power, there is always COBOL with its ALTER
statement.

....and its prohibition almost invariably appears on page 1 of any COBOL
in-house coding standards document. :)

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: normal service will be restored as soon as possible. Please do not
adjust your email clients.
 

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

Forum statistics

Threads
473,743
Messages
2,569,478
Members
44,898
Latest member
BlairH7607

Latest Threads

Top