1

In a program I'm writing, there is a call to scanf(), which reads a long decimal number storing money.

 do {
     fflush(stdin);
     printf("What is the number?\n");
 } while (scanf("%Lf", &n.amt) == 0);

However, when debugging, I see n.amt is equal to 0. If I entered 100, why is it showing that it read a zero? At first, I was using a float, I changed that to a long double, but this problem persists.

This is also evident, because this data is later written to a file, and 0 is also written to there.

lost_in_the_source
  • 10,998
  • 9
  • 46
  • 75
  • What's a "long float"? Try `long double`. Or just use `"%f"` instead of `"%Lf"` – Nemo Dec 23 '14 at 23:53
  • `fflush(stdin)` causes undefined behaviour – M.M Dec 24 '14 at 00:01
  • `fflush(stdin)` does **NOT** extract remaining characters from `stdin`. Use `getchar()`. – David C. Rankin Dec 24 '14 at 00:01
  • this line: 'fflush(stdin);' should never be used. for several reasons, 1) it is specifically meaningless in the latest standards 2) fflush is for outputs, not inputs. – user3629249 Dec 24 '14 at 00:49
  • 1
    regarding this line: '} while (scanf("%Lf", &n.amt) == 0);' there are a few problems: 1) to skip left over white space, like a newline, the format string should contain a leading ' ' (space) 2) the returned value from input functions should always be checked to assure the operation was successful. In your example, the operation was not successful 3) there may be a presidence problem with '&n.amt' suggest using '&(n.amt)' – user3629249 Dec 24 '14 at 00:52
  • See [Using `fflush(stdin)`](http://stackoverflow.com/questions/2979209/using-fflushstdin) for a more nuanced view of whether `fflush(stdin)` works or not. On Windows it does; elsewhere, empirically, despite mentions in some Linux man pages, it does not. – Jonathan Leffler Dec 24 '14 at 00:55
  • Note that you can exit your loop because `scanf()` returned EOF or because it returned 1 (meaning it successfully scanned a value). – Jonathan Leffler Dec 24 '14 at 00:56
  • @user3629249 The leading space is not needed with `"%Lf"`. Leading whitespace is consumed by all `scanf()` specifiers except 3: `"c n ["`. – chux - Reinstate Monica Dec 24 '14 at 01:32
  • Suspect the "100" is not the first thing entered in the program. It is previous left-over data entry that is causing the trouble. Consider a [mcve](http://stackoverflow.com/help/mcve) – chux - Reinstate Monica Dec 24 '14 at 01:41

2 Answers2

1

There are several problems with the code.

First, as already been noted, fflush(stdin) is undefined behavior. Although its behavior for input stream might be documented for a particular implementation (with glibc, fflush(stdin) flushes unread input), this is not portable behavior.

Removing fflush() won't be enough. If scanf() gets a format specification like '%lF', and after skipping any whitespace in the input stream the next character will not be convertible to the requested format specification (it's not a digit, a decimal point, or +/- sign for the '%lF' format specification, for example) the next character actually remains unread, and if there are no other format specifications that have been successfully converted, scanf() would return 0. If you loop around again, the unread character will still not be convertible, so you end up with an infinite loop.

I never liked using scanf(), because handling this kind of an error condition was always awkward.

I prefer to use fgets(), to read the entire line of input into a buffer, and, if you insist, use sscanf() to parse it. With that approach, the error recovery semantics will be easy to implement.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • What I wrote in the last paragraph in my answer. – Sam Varshavchik Dec 24 '14 at 00:18
  • 1
    See [Using `fflush(stdin)`](http://stackoverflow.com/questions/2979209/using-fflushstdin) for a more nuanced view of whether `fflush(stdin)` works or not. On Windows it does; elsewhere, empirically, despite mentions in some Linux man pages, it does not. – Jonathan Leffler Dec 24 '14 at 01:01
1

When getting input for a numeric, it is a bit tricky to protect against both an empty string ([enter]) or garbage input without causing your input to hang on a blank line when using scanf. The following reads the value as a string and protects against no input and garbage input without a hang. It will only proceed if there is a valid conversion to a double value with strtod (you can substitute strtold for long double):

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

int main () {

    char amount[50] = {0};
    char *ep = NULL;
    double n_amt = 0.0;

    do {
        /* protect against [enter] and garbage as input */
        while (printf ("\nEnter the amount of money in the transaction: $ ") &&
                scanf ("%49[^\n]%*c", amount) == 0 && getchar()) ;

        /* convert to double */
        n_amt = strtod (amount, &ep);

        /* loop if no valid conversion */
    } while ( &amount[0] == ep );


    printf ("\n n.amt = %lf\n\n", n_amt);

    return 0;
}

output:

$ ./bin/scanf_double

Enter the amount of money in the transaction: $

Enter the amount of money in the transaction: $ lsdkfj

Enter the amount of money in the transaction: $ 123.45

 n.amt = 123.450000

Note: you are better off handling money as integer values rather than floating point. You are also better off using fgets or getline to read amount rather than scanf, but the purpose of the example was to provide a scanf solution.

equivalent input using fgets

do {
    printf ("\nEnter the amount of money in the transaction: $ ");
    fgets (amount, MAXL, stdin);
    n_amt = strtod (amount, &ep);
} while ( &amount[0] == ep );
David C. Rankin
  • 81,885
  • 6
  • 58
  • 85