0

I'm creating a temperature converter in C. Basically, you input a minimum and maximum value in degrees Celsius, along with a step, and it displays that information in a list, along with the Fahrenheit equivalent. On some occasions, I have noticed the last Fahrenheit entry not being displayed when it should. For example, when you input a lower limit of 10, a higher limit of 30, and a step of 4, it cuts off the last Fahrenheit temperature. I know it's something to do with the last while loop, but I just can't figure it out.

#include <stdio.h>

int main (int argc, const char * argv[]) {
double l, h, s;
double lf, hf, sf;
/* Number rows in tables */
int num1, num2;
num1 = 1;
num2 = 1;

/* Lower limit input */
printf("Please give a lower limit: ");
scanf("%4lf", &l);
while (l < 0) {
        printf("Lower limit must be greater than 0: ");
        scanf("%4lf", &l);
}

/* Stores value for Fahrenheit conversion */
lf = l;

/* Higher limit input */
printf("Please give a higher limit: ");
scanf("%4lf", &h);
while (h <= l) {
        printf("Higher limit must be greater than lower limit: ");
        scanf("%4lf", &h);
}

while (h >= 50000) {
        printf("Higher limit must be less than 50000: ");
        scanf("%4lf", &h);
}

hf = h;

/* Step input */
printf("Please input step: ");
scanf("%4lf", &s);
while (s <= 0) {
        printf("Step must be greater than 0: ");
        scanf("%4lf", &s);
}

while (s >= h - l) {
        printf("Step must be less than the difference in temperatures: ");
        scanf("%4lf", &s);
}

sf = s;

/* Celsius table */
printf("\nCelsius\n-------\n");
while (l <= h) {
    printf("%i. %4lf\n", num1, l);
    num1++;
    l = l + s;
}

/* Fahrenheit table */
printf("\nFahrenheit\n----------\n");
/* Converts Celsius to Fahrenheit */
lf = (lf * 1.8) + 32;
hf = (hf * 1.8) + 32;
sf = sf * 1.8;
printf("Lower input: %4lf\n", lf);
printf("Higher input: %4lf\n", hf);
printf("Step: %4lf\n----------\n", sf);
/* This while loop sometimes cuts off the last entry */
while (lf <= hf) {
    printf("%i. %4lf\n", num2, lf);
    num2++;
    lf = lf + sf;
}

return 0;

}
Ben
  • 3
  • 2

4 Answers4

3

The problem is with comparing the doubles, you might get into a situation where something like 10 + 1.8 evaluates to 11.800000001 and thus missing the end value.

The solution to your problem would be to first calculate the number of steps:

int steps = (h - l) / s + 1; //Might want to apply rounding

And then use a for/while loop over the integer variable instead:

for (int i = 0; i < steps; ++i) {
    double t = l + (h - l) * i / (steps - 1);
}

for (int i = 0; i < steps; ++i) {
    double tf = lf + (hf - lf) * i / (steps - 1);
}
Andreas Brinck
  • 51,293
  • 14
  • 84
  • 114
  • This. You should use integers for ordinals, like the number of times to execute a loop. – caf Aug 18 '11 at 09:46
  • I understand the code calculating the number of steps, but the for/while loop is less clear. double t displays inconsistent results. For example, inputting a lower limit of 10, a higher limit of 20, and a step of 2, displays 10.000000, 12.500000, 15.000000, etc. – Ben Aug 19 '11 at 01:27
  • @Ben Should have been a `+ 1` in the `steps` calculation, fixed. – Andreas Brinck Aug 19 '11 at 06:57
2
while (lf <= hf) 

You are comparing Double values, You will face problems of Precison & Rounding Errors, Most likely that causes the last iteration to not execute at all.

This answer of mine, should be a good read.

Community
  • 1
  • 1
Alok Save
  • 202,538
  • 53
  • 430
  • 533
  • and what is the solution to this problem? – duedl0r Aug 18 '11 at 08:19
  • To add to this, generally with precision you'll want to compare to an epsilon: some small value that shows the tolerance. So instead of `while (lf <= hf)` you might try `while (lf - hf <= .001)` – Stefan Mai Aug 18 '11 at 08:22
  • @duedl0r: I updated the answer with an link, which will explain you the problem and the solution. – Alok Save Aug 18 '11 at 09:26
  • This explains why the author used integers for his solution (this is a programming challenge from a book). My code works, but is far from elegant. I like Stefan Mai's suggestion. – Ben Aug 19 '11 at 01:37
  • @Ben: Did you read the link to the previous answer? Actually, It suggests `abs(x - y) < epsilon`, which is more or less same as what Stefan suggested, just it is more in a C++ way way of doing things because that Q was tagged c++. – Alok Save Aug 19 '11 at 03:50
  • @Als: Sorry, I didn't mean to imply your answer was inelegant if that's what you're thinking. This temperature converter was a programming challenge from a book and I was reflecting on how I approached the program. The solution to the challenge used half the amount of code I used. Reading over my previous comment, I can see how it might be taken the wrong way. Thank you for the link! – Ben Aug 22 '11 at 03:35
  • @Ben: No worries, As long as your problem got solved, it doen't matter who helped solve it, SO is a community effort, Glad could do my bit to help you. All the Best. :) – Alok Save Aug 22 '11 at 03:40
0

Choose a precision and compare it with the difference between hf and lf(abs value). Also keep in mind with a random fixed step you will not always reach the top value of the interval. it might work if step divides h-l but it won't work otherwise.

Sleeperson
  • 612
  • 3
  • 8
0

Directly after your last while loop, add the following code (and don't forget to #include assert.h):

printf("lf = %f, hf = %f\n", lf, hf);
assert(lf == hf);

you should get this output:

Celsius
-------
1. 10.000000
2. 14.000000
3. 18.000000
4. 22.000000
5. 26.000000
6. 30.000000

Fahrenheit
----------
Lower input: 50.000000
Higher input: 86.000000
Step: 7.200000
----------
1. 50.000000
2. 57.200000
3. 64.400000
4. 71.600000
5. 78.800000
lf = 86.000000, hf = 86.000000
Assertion failed: lf == hf, file randomdudescode.c, line 77

Which is incredibly confusing, because obviously lf == hf. This illustrates one of the quirks of C and floating point tomfoolery in general. You have to deal with rounding errors and imprecision.

TheIronKnuckle
  • 7,224
  • 4
  • 33
  • 56