Trying to write a program that reads input from a file and uses that to calculate a water bill using a function. Whenever I run the program and print the results of the function, it either prints 0, -0, or a very large number. I know the formulas I used are correct because I put them in a calculator and it worked fine, so the I think the problem is in my function or my function call.
first few lines of the file:
g 5000
B 1250
M 50
r 120
this is the output when I run my program:
Type Water usage Cost including tax
g 5000 0.000000
B 1250 -0.000000
Wrong user type.
M 50 0.000000
r 120 377145544395 (this number is much larger, I didn't want to copy the entire thing)
and my code:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <math.h>
double water_billcalculation(char user_type, double water_use);
int main(void) {
FILE* water;
water = fopen("water_usage.txt", "r");
char user_type;
double water_use;
printf("Type\tWater usage\tCost including tax\n");
while (fscanf(water, "%c %d\n\n", &user_type, &water_use) != EOF) {
printf("%c\t%d\t\t%lf\n", user_type, water_use, water_billcalculation(user_type, water_use));
}
return(0);
}
double water_billcalculation(char user_type, double water_use) {
double tc = 0, x;
if (user_type == 'G' || user_type == 'g') {
x = water_use * .035;
tc = x (x * .087) 3.75;
}
else if (user_type == 'B' || user_type == 'b') {
x = water_use * .0553;
tc = x (x * .087) 17.25;
}
else if (user_type == 'R' || user_type == 'r') {
if (water_use <= 400) {
x = water_use * .04;
tc = x (x * .087) 13.5;
}
else if (water_use > 400 && water_use <= 700) {
x = water_use * .062;
tc = x (x * .087) 13.5;
}
else {
x = water_use * .12;
tc = x (x * .087) 13.5;
}
}
else {
printf("Wrong user type.\n");
}
return(tc);
}
CodePudding user response:
while (fscanf(water, "%c %d\n\n", &user_type, &water_use) != EOF) {
printf("%c\t%d\t\t%lf\n", user_type, water_use, water_billcalculation(user_type, water_use));
}
Your format strings are messed up. The %d
format specifier corresponds to a variable of type int
(i.e. it needs an int
for printf, and a pointer to int
for scanf). But the variable water_use
is of type double
. This mismatch causes undefined behavior, which can include incorrect values. The type double
should use %lf
. (Other variants are possible, e.g. %lg
, but I will not discuss them here. For printf
the l
is optional but for scanf
it is mandatory, so we might as well use it consistently.).
Also, fscanf
only returns EOF if an error occurred, or the end of the file was reached. If the file is mis-formatted and only one (or zero) of the input items are successfully matched, then fscanf
may return 0 or 1 (number of items matched), in which case your program would go ahead with stale or uninitialized data in user_type
and water_use
. So to be safe, you should only proceed if fscanf
returns 2.
As M.M. points out, \n\n
in the fscanf
format string can be replaced by a single \n
or any other whitespace, but perhaps as it is, it helps document the file format that's expected. So I won't change that.
So change this to
while (fscanf(water, "%c %lf\n\n", &user_type, &water_use) == 2) {
printf("%c\t%lf\t\t%lf\n", user_type, water_use, water_billcalculation(user_type, water_use));
}
A good compiler will warn you about the format string problem. gcc -Wall
says:
water-orig.c: In function ‘main’:
water-orig.c:18:31: warning: format ‘%d’ expects argument of type ‘int *’, but argument 4 has type ‘double *’ [-Wformat=]
18 | while (fscanf(water, "%c %d\n\n", &user_type, &water_use) != EOF) {
| ~^ ~~~~~~~~~~
| | |
| int * double *
| %le
water-orig.c:19:22: warning: format ‘%d’ expects argument of type ‘int’, but argument 3 has type ‘double’ [-Wformat=]
19 | printf("%c\t%d\t\t%lf\n", user_type, water_use, water_billcalculation(user_type, water_use));
| ~^ ~~~~~~~~~
| | |
| int double
| %f
which would have explained the problem immediately and saved you an hour of writing this post and waiting for an answer.
CodePudding user response:
Your format string is messed up, with the wrong type and spurious spaces, and you're failing to check for invalid input. You should have:
while (fscanf(water, " %c%lf", &user_type, &water_use) == 2) {
- to read a
double
you want%lf
not%d
(your compiler should be giving you a warning about this; if not you want to enable warnings) - you should compare the result of the scanf to the number of things you expect to be correctly parsed. This will deal properly with (by exiting the loop) reaching the end of file or having malformed data in the file
- you should not have spurious whitespace in the format. You want a leading space to ignore any whitespace after the previous entry (any blank lines, for example), and you don't particularly want to deal with whitespace after an entry until you need the next entry.