I am taking an introductory course in C programming and using the Miracle C Compiler for assignments.

I have been trying to find a way to "validate" user input a float variable.

after declaring and initializing a variable called float fSale 0;

// I try variations of

printf ("\nEnter Sale amount $", fSale)
scanf("%f", &fSale);

{
if (fSale > 0)

printf ("\nSale amount %.2f", fSale);

else

printf ("\nMust be a positive amount\n");
{

This does NOT prudce the desired validation. Any suggestions for a newbie ?

printf( "Enter sale amount $: " );
fflush( stdout );   /* because there's not newline at the end of printf string argument */
if ( scanf("%f", &fSale) ) /* scanf returns a positive int if successful, the if checks that */
{
     if ( fSale > 0.0 )
     {
          printf( "Sale amount: %.2f\n", fSale );
     }
     else
     {
          puts( "Must be a positive amount" );
     }
}
else
{
     puts( "Invalid input" );
}

Avoid using Miracle C. The Miracle would be if it works correctly at all. There are plenty good free compilers to choose. Look in the starting C post at the beginning of the forum.

Avoid using Miracle C. The Miracle would be if it works correctly at all. There are plenty good free compilers to choose. Look in the starting C post at the beginning of the forum.

I went to their website for the first time today. It claims MC is still under development, but nowhere is there a list of changes, nor a date. The site could be 15 years old...

The compiler is also shareware and cost $19 to register.

I agree with Aia, though. Historically, it's been terrible so get a good free compiler (not necessarily in recommended order):
Code::Blocks
MSVC++ Express
Open Watcom C++/Fortran
Bloodshed DevC
Turbo C++ Explorer 2006
Borland 5.5

AIA:

Thanks so much, this is a differenht approach than I was taking and gives me an idea to sue another statement.

How about a "do while" loop ?

i tend to avoid "do/while" loops. "do" statements are kind of unpopular with most c programmers unless theres a compelling reason to use them.

just use a while statement to get the same effect.

as for validating floats... stay away from scanf. I would personally input a string, validate that it only contains the type of characters desired (within the while loop), then convert it to a float using "atof" after you're certain the input is valid.

...

as for using MiracleC, it's probably okay for true beginners to learn on... meaning, for very simple programs. But like others mentioned, it's not exactly reputable... once you get used to the basics of C programming, do please get a worthwhile compiler.

Visual C++ Express edition is available free from Microsoft.. even if you're not a fan of the Redmond Software Giant, it is *free* and it's solid, and it's one of the industry standards.

the last thing you want to do as a student is try and debug some crappy compiler bugs :-)

here's a bit more help, with the validation part, since my explanation is not real clear. this code is not complete. you'll have to declare variables, how to get the inputs, and determine what to do with the results (i just put vague "returns"), and such like that.

for (a=0; a<strlen(floatStr); a++) {
    if (a==0) {
        if (floatStr[0]=='+' || floatStr[0] == '-') ;

        else if (floatStr[0] == '.')
            foundDecimal = 1;

        else if (floatStr[0]<'0' || floatStr[0] > '9') {
            invalid = 1;
            break;
        }
    }
    else if (a>0) {
        if (!foundDecimal && floatStr[a] == '.') 
            foundDecimal = 1;

        else if (floatStr[a] < '0' || floatStr[0] > '9') {
            invalid=1;
            break;
        }
    }
}

if (invalid) {
    printf("you must enter a valid floating point number\n");
    return 0;
}
else {
    printf("the value: %f is valid\n", atof(floatStr));
    return 1;
}

AIA:

Thanks so much, this is a differenht approach than I was taking and gives me an idea to sue another statement.

How about a "do while" loop ?

Definitely!. A loop of any kind on condition of a valid input would allow you to continue to prompt until such condition is met.
Considerate an if statement as a one time deal and a loop as constant nagging until it gets what it wants. ;)

AIA:

Here is what I understand your example to mean:

#include <stdio.h>
void main()
{
float fSale;
fSale = 0;

printf("Enter sale amount $: ");
fflush(stdout);   /* because there's not newline at the end of printf string argument */
if (scanf("%f", &fSale)) /* scanf returns a positive int if successful, the if checks that */
{
     if ( fSale > 0.0 )
     {
          printf("Sale amount: %.2f\n", fSale);
     }
     else
     {
          puts("Must be a positive amount");
     }
}
else
{
     puts("Invalid input");
}
getch();
}

Two questions:
1) what does fflush (stdout) do ?
2) Whe would the second "else" come into play for "invalid inoput". I compiled and ran this several tiems and could not trigger the secodn else statement.

thanks again

Edited 3 Years Ago by mike_2000_17: Fixed formatting

Jephthah:

Thanks for your valdiation sequence. It works but is a little overkill for my need this week. Out lessons have included validating integers and if I understand your intent, you rpove the float is positive by placing the input betwen a "range" of poitive integers ??

AIA:
Here is what I understand your example to mean:
void main()
getch();

You are hurting me. I'd never write those abominations. ;)
main returns an int, therefore you need to write int main( void )
getch() is a non-standard function. Makes code not portable. I use getchar() instead.

>1). what does fflush (stdout) do ?

It makes sure that the standard output stream is fflush or showed in the screen.
It is guaranteed to do that if a \n ( newline ) were as part of the argument of the printf, however since we want the prompt to hang right after the sentence, there's not newline in the literal string inside printf. It is good practice even when some compilers do not care either way.
Notice how I use some puts instead of printf. puts() adds automatically a new line to the literal string, in every call. And since I didn't need to use the % format operator I chose puts.

>2). Whe would the second "else" come into play for "invalid inoput". I compiled and ran this several tiems and could not trigger the secodn else statement.

Enter a phrase or some characters instead of a real number.

Thanks for your valdiation sequence. It works but is a little overkill for my need this week. Out lessons have included validating integers

i dont know about your class' requirements or the understanding your teacher may have with you... but in industry its impossible to properly validate any function that uses "scanf". no one uses "scanf" because its so unreliable.

the problem with scanf, is if you enter an incorrect value, it will crash.

any program that crashes on a possible set of inputs, can not be validated; that is the whole point of validation, to not allow inputs that crash the program.

if I understand your intent, you rpove the float is positive by placing the input betwen a "range" of poitive integers ??

no, my example just checks for invalid characters. a basic float can have an optional + or - sign (but only in the first location), followed by any number of digits (up to length of string) and one optional decimal point somewhere in the string. a more robust example would strip whitespace and allow for exponential notation, but that's another issue.

anyhow, my point is that to properly validate a float, you must checks for letters and other non-numeric characters, more than one decimal point, or a + or - that is not at the beginning ... any of these items are invalid. try putting any of those into scanf and you have a program crash.

Not quite, jephthah. scanf() generally will crash when inputting characters into a %s format and the user types more characters than the variable can hold. In most other cases (numbers) scanf() simply stops at the first invalid character for the format. This will cause problems on the next read if more numbers are needed. But by checking the return code you can at least tell if the input failed or not.

I will agree it's a crap shoot using scanf() (see this series) and it should be avoided. And it should never be used to input strings.

Not quite, jephthah.

yes quite, Walt.

any industry C programmer will tell you to never use scanf, even for numbers, because if anyone puts a character in there, or any other invalid non-numeric...

the program *crashes*

as in, grinds to a screeching halt in the singlemost castastrophic event possible, and it's just begging to happen with a scanf()

now, if they want to use it in class, thats fine. but if the lesson is specifically about validating input then I dont know how anyone can reasonably begin to think that scanf() is a possible solution for a validation excercise.

I'm saying that scanf() is exactly what shouldn't be used in such an excercise, and i would expect that students will fail the test if they choose to use it in this specific case.

yes quite, Walt.

any industry C programmer will tell you to never use scanf, even for numbers, because if anyone puts a character in there, or any other invalid non-numeric...

I am an industry programmer, and I never said scanf() should be used. Notice I said "I will agree it's a crap shoot using scanf() "? I agreed with you.

What I was commenting on was

the program *crashes*

.
Crash does not mean the program keeps running but loops forever. Crash is a term used in the industry to explain why a program exits prematurely, usually with a dump, or a system message telling you to send Microsoft the information.

as in, grinds to a screeching halt in the singlemost castastrophic event possible, and it's just begging to happen with a scanf()

Rarely does scanf() cause this problem you describe. It is known to loop forever when invalid numbers are entered. When invalid strings are entered it can cause a crash (rarely "catastrophic" thanks to memory protection) or simply overwrite values causing an invalid execution, and possibly a crash in response.

now, if they want to use it in class, thats fine. but if the lesson is specifically about validating input then I dont know how anyone can reasonably begin to think that scanf() is a possible solution for a validation excercise.

I'm saying that scanf() is exactly what shouldn't be used in such an excercise, and i would expect that students will fail the test if they choose to use it in this specific case.

I absolutely agree. Did you bother to read my link? You would have seen that I agree. I was commenting your invalid use of the word *crash*, that's all. Sorry if I wasn't clear.

okay, okay. I used the word "crash" imprecisely. An "infinite loop" is not technically a "crash".

but when i have to hit Control-C or CTRL-ALT-DEL to stop an out of control program... my engineering managers and VP of production will want to know why my crappy program "crashed" the entire production line and put 30 assembly workers off their tasks.

they will not care to investigate rhetorical turns of phrase as to what, exactly, "crash" means.

still, you do get the point for precision.

gg

OK, taking some of your suggestions I came up with the following and it works except, if I enter a postive float amount as $10.20.1, it accepts this invalid inout even though the program only calculates on 2 decimal places. Any suggestions ?

#include <stdio.h>
void main ()
{
//Variable declarations
float fSale;
float fDtax;
float fEtax;
float fLtax;
float fResultD;
float fResultE;
float fResultL;
char cResponse = '\0';
float fSaleandTaxD;
float fSaleandTaxE;
float fSaleandTaxL;



//variable initializations
fSale = 0;
fDtax = .0725;
fEtax = .075;
fLtax = .0775;
fResultD = 0;
fResultE = 0;
fResultL = 0;
fSaleandTaxD = 0;
fSaleandTaxE = 0;
fSaleandTaxL = 0;


//Validate entry for a positive float amount and if a zero amount or a negative amount are entered, return to entry screen
printf("\nEnter Total Sale Amount $", fSale);
scanf("%f", &fSale);


if (fSale <= .00) {
printf("\nInvalid amount, please enter a positive value: ");
printf("\n\n");


do {
//Request total amount of the sale to be taxed
printf("\nEnter Total Sale Amount $", fSale);
scanf("%f", &fSale);
}
while (fSale <= .00);
}
printf("\n\n");
//compute sales taxes based on location
fResultD = fSale * fDtax;
fResultE = fSale * fEtax;
fResultL = fSale * fLtax;
//Sale plus Sales Tax totals by location
fSaleandTaxD = (fSale * fDtax) + fSale;
fSaleandTaxE = (fSale * fEtax) + fSale;
fSaleandTaxL = (fSale * fLtax) + fSale;
//Then as query and response format
printf("\nChoose Store Location:\n");
printf("\n\n");
printf("D for Del Mar\n");
printf("E for Encinitas\n");
printf("L for La Jolla\n");
printf("\n\n");
printf("Enter your Selection: ");
scanf("%c", &cResponse);
printf("\n\n");
if (cResponse == 'D'){
printf("\nDel Mar Sales Tax is $%.2f\n", fResultD);
printf("\n\n");
printf("Del Mar total with Tax is $%.2f", fSaleandTaxD);
}


if (cResponse == 'E') {
printf("\nEncinitas Sales Tax is $%.2f\n", fResultE);
printf("\n\n");
printf("Encinitas total with Tax is $%.2f", fSaleandTaxE);
}


if (cResponse == 'L'){
printf("\nLa Jolla Sales Tax is $%.2f\n", fResultL);
printf("\n\n");
printf("La Jolla total with Tax is $%.2f", fSaleandTaxL);
}



getch();


return();
}

Edited 3 Years Ago by happygeek: fixed formatting

apparently a second decimal will not crash/hang the program

if you put a valid value in scanf, followed by something invalid, it will accept the valid part and ignore the invalid part. but if you start with something invalid, it will go haywire

12.34 valid part = 12.34
12.34.5 valid part = 12.34
12xz99 valid part = 12 (12.00)
12..34 valid part = 12. (12.00)
$12.34 invalid -> program goes out of control.

so we can argue the details, but the point i'd still say is "don't ever use scanf() in the real world" ... however, if your instructor is okay with you using it, then enjoy it while you can :)

another couple things i see with your code:

the line

return();

is invalid... you don't return a void. you also only use () if you need to evaluate an expression before returning the value.

and the line

scanf("%c", &cResponse);

is doomed to fail. consider getc() or fgets() instead

>"don't ever use scanf() in the real world" ...
Without any error checking.

are we still here?

look, in the real world (i work for a medical device manufacturer), I would be run out of the plant if i tried to pass a program to the assembly line that used

scanf("%f",&myFloat);

please put all the "error checking" you like and then tell me how the input:

$12.34

works out for you.

>are we still here?
Yes, and apparently we'll stay here for as long as it takes everyone to understand the point of scanf.

>I would be run out of the plant if i tried to pass
> a program to the assembly line that used <snip scanf code>
I have a similar practice in the real world (I work for a C and C++ compiler vendor): don't use scanf unless you wrote it. That works out well because only a handful of our programmers have touched the scanf source, and they're the ones who can be trusted to use it properly. ;)

>please put all the "error checking" you like and then tell me how the input:
>$12.34
>works out for you.
It works just peachy. I even took it to the next level and accepted negative currency values as they would appear on a ledger:

#include <stdio.h>

#define POSITIVE_LEAD "$" /* Leading char for positive currency */
#define NEGATIVE_LEAD "(" /* Leading char for negative currency */

#define length(a) ( sizeof (a) / sizeof *(a) )

int main ( void )
{
  const char *currency_fmt[] = {
    "%[" POSITIVE_LEAD "]%f",   /* Currency format (positive) */
    "%[" NEGATIVE_LEAD "]$%f)", /* Currency format (negative) */
  };

  char lead = EOF;
  float value;
  int i;

  /* Try to read an expected currency format */
  for ( i = 0; i < length ( currency_fmt ); i++ ) {
    if ( scanf ( currency_fmt[i], &lead, &value ) == 2 )
      break;
  }

  if ( i < length ( currency_fmt ) ) {
    /* Fix the sign internally */
    if ( lead == NEGATIVE_LEAD )
      value = -value;

    printf ( "Received $%.2f\n", value );
  }
  else
    fputs ( "Invalid input\n", stderr );

  return 0;
}

With proper error handling, there's nothing wrong with scanf. The problem is people who don't realize that scanf is designed for strictly formatted input. If the input isn't expected to be strictly formatted, such as user input, scanf should be avoided in favor of more robust interactive input logic.

I imagine it's the fact that scanf reads from stdin that confuses so many people. For a lot of C programmers, stdin is the interactive terminal, and because scanf reads from stdin, clearly it's meant for interactive user input.

If you've ever worked with console filters, you know that they're best written using stdin so that they can be chained together with pipes from the console as well as directed from and to files where strict formatting is possible.

Comments
+Rep is probably long overdue, but this is a scanf discussion, so it now becomes imperative.
zing

The problem is people who don't realize that scanf is designed for strictly formatted input.

Okay. I'll buy that. nice code, by the way

The code presented by Jepthah will not correctly vaildate 12..3 or 12.b.rr.3 and the like

>The code presented by Jepthah will not correctly vaildate 12..3 or 12.b.rr.3 and the like
Validating floating-point is surprisingly difficult, especially if you choose to allow exponent formatting. I imagine jephthah was keeping the algorithm simple, because to do it right means a lot of added complexity. Here's something relatively complete and much more formalized than jephthah's, and I still wouldn't claim that it allows all valid strings and catches all invalid ones:

#include <ctype.h>

static int is_sign ( int ch )
{
  return ch == '-' || ch == '+';
}

static int is_fraction_boundary ( int ch )
{
  /* Allow for variations such as "-.1" and ".1" */
  return isdigit ( ch ) || ch == EOF || is_sign ( ch );
}

/*!
  <summary>
  Validates a string for with floating-point format rules
  </summary>
  <param name="src">The string to validate</param>
  <returns>
  A pointer to the character at which validation stopped. For
  successful conversions on a non-empty string, this will be a
  pointer to the null character.
  </returns>
  <remarks>
  Conversion rules (grammar):

  floating-constant:
    sign(opt) fractional-constant exponent-part(opt)
    sign(opt) digit-sequence exponent-part
    sign(opt) digit-sequence

  fractional-constant:
    digit-sequence(opt) . digit-sequence
    digit-sequence .

  exponent-part:
    e sign(opt) digit-sequence
    E sign(opt) digit-sequence

  sign: one of
    + -

  digit-sequence:
    digit
    digit-sequence digit
  </remarks>
*/
const char *is_float_format ( const char *src )
{
  int in_exponent = 0; /* When true, exponent-part rules apply */
  int in_fraction = 0; /* When true, fractional-constant rules apply */
  int seen_digit = 0;  /* When true, src contains a digit */
  int last = EOF;      /* The previous valid character */
  int ch = EOF;        /* The current (possibly invalid) character */

  while ( *src != '\0' ) {
    /* Force lower case for simplified exponent testing */
    ch = tolower ( *src++ );

    if ( isdigit ( ch ) )
      seen_digit = 1;
    else {
      if ( is_sign ( ch ) ) {
        /* Sign allowed as first the part in floating-constant */
        int float_boundary = last == EOF;

        /* Sign allowed immediately after 'e' in exponent-part */
        int exp_boundary = in_exponent && last == 'e';

        if ( !float_boundary && !exp_boundary ) {
          /* The sign isn't in an expected location */
          return --src;
        }
      }
      else if ( ch == 'e' ) {
        if ( in_exponent || !seen_digit ) {
          /* fractional-constant or digit-sequence required */
          return --src;
        }

        in_exponent = 1;
      }
      else if ( ch == '.' ) {
        if ( in_fraction || in_exponent
          || !is_fraction_boundary ( last ) )
        {
          /* The decimal isn't in an expected location */
          return --src;
        }

        in_fraction = 1;
      }
      else {
        /* Not an expected non-digit character */
        return --src;
      }
    }

    /* Force lower case for simplified exponent testing */
    last = tolower ( ch );
  }

  /* Valid so far; make sure we're ending successfully */
  if ( ch != EOF ) {
    if ( is_sign ( ch ) || ch == 'e' ) {
      /* Can only end on '.' in "digit-sequence ." */
      --src;
    }
    else if ( !seen_digit ) {
      /* At least one digit is required */
      --src;
    }
  }

  return src;
}

Not hard to see why sscanf, strtod, or some other equivalent standard function is recommended for validation, is it? ;)

The code presented by Jepthah will not correctly vaildate 12..3 or 12.b.rr.3 and the like

awesome first post, dude.... to a thread that was dead for 2 1/2 months.

did you have a better solution?


.

>to a thread that was dead for 2 1/2 months.
The comment was relevant, so it doesn't matter how old the thread is.

>did you have a better solution?
I'd say it was a fair warning to those who might unknowingly use the algorithm thinking that it's bulletproof, whether he had a better solution or not.

eh, fair enough.

i never said it was bulletproof. it's far too simplistic to be anywhere near that, yet still too complicated for some of the "hello world" type programs here.

perhaps my real problem is that the poster forced me to review some of my earliest posts here, making me think "eww. did i really post that?"

i miss your chainsaw, by the way


.

>perhaps my real problem is that the poster forced me to review some
>of my earliest posts here, making me think "eww. did i really post that?"
Feh, welcome to the club. I wish I could delete some of my oldest posts on cprog because they're so horrible.

>perhaps my real problem is that the poster forced me to review some
>of my earliest posts here, making me think "eww. did i really post that?"
Feh, welcome to the club. I wish I could delete some of my oldest posts on cprog because they're so horrible.

Why do you wish to rob us of such an inspiring testament of maturity?

I would have cause for concern, if, in a not too distant future, a look at a piece of code I write today, it would not raise objections on my part.

This article has been dead for over six months. Start a new discussion instead.