3

When writing a program in C to convert celsius to fahrenheit, the following formula gives the incorrect output:

    int fahr = 9 / 5 * celsius + 32;

Now, I understand that this is probably an issue with 9/5 being interpreted as an integer, but what I don't understand is that using double or floatit still gives the same incorrect output.

Oddly enough the following formula gives the correct output despite also setting the type to int:

int fahr = celsius / 5 * 9 + 32;

Furthermore, i've noticed even something as simple as the below, when the type is set to double, still gives the output as 1.0 instead of 1.8:

 double x = 9 / 5;
 printf("%lf\n", x);

I've read this thread:

C program to convert Fahrenheit to Celsius

but I still don't understand why int fahr = celsius / 5 * 9 + 32; works but not int fahr = 9/5 * celsius+32; ?

Community
  • 1
  • 1
Martin
  • 1,060
  • 2
  • 16
  • 28

3 Answers3

9

You're doing math with integers. This expression:

9 / 5

Yields 1 in C. When you say you used double or float, you probably just changed the type of fahr, which doesn't do anything to the operations taking place on the right side of the assignemtn operator. To get the right behaviour, you need to make at least one of those constants a double, too:

9.0 / 5

Likewise, in this statement:

double x = 9 / 5;

You're still doing integer math, and then assigning the result to a double variable. There isn't anything else going on. You'll get the right answer by doing one of these:

double x = 9.0 / 5;
double x = 9 / 5.0;
double x = 9.0 / 5.0;

The reason this expression:

int fahr = celsius / 5 * 9 + 32;

appears to work is just an order of operations thing - here you divide the input by 5 and then multiply by nine, rather than doing the constant operation first. You'd still get more accurate answers by doing:

int fahr = celsius * 9 / 5 + 32;

Besides that, you could also do floating point math in this expression:

int fahr = celsius * 9.0 / 5 + 32;

If you want to do the original calculation using integers, you certainly can - you just need to multiply before dividing:

int fahr = 9 * celsius / 5 + 32;

This expression is equivalent to one of the ones used above.

Carl Norum
  • 219,201
  • 40
  • 422
  • 469
  • I understand that. Please read the whole post. – Martin Apr 18 '13 at 00:34
  • @Tangler, I don't think you do. I've edited to explain better. – Carl Norum Apr 18 '13 at 00:34
  • Ok, so why does this work: int fahr = celsius / 5 * 9 + 32 ? – Martin Apr 18 '13 at 00:38
  • I added another edit to cover that. It's because the operations are done left to right. – Carl Norum Apr 18 '13 at 00:38
  • Ok thanks. In regards to your statement "here you divide the input by 5 and then multiply by nine, rather than doing the constant operation first.", i'm still a bit unclear, because I have celsius also set as an int, therefore it's really just dividing to integers just like 9/5. So are you saying that using a int variable instead of a constant is what's making the difference (even though the variable is assigned the same value as the constant)? – Martin Apr 18 '13 at 00:52
  • I'm not sure I follow. The point is that `celsius / 5` happens first, then the result of that is multiplied by `9`. So for some values of `celsius` you might well get the right result. In the other case, `9/5` happens first, giving `1`, and then that gets multiplied by `celsius`. – Carl Norum Apr 18 '13 at 01:55
1

The type of each expression or subexpression is (in most cases) evaluated without regard to the context in which it appears.

In this declaration:

double x = 9 / 5;

the initialization expression is 9 / 5; it consists of two int expressions and a division operator. Since the operands of / are of type int it's an int division, resulting in an int value. Since integer division truncates, the result is 1, of type int.

The result of that expression is then used to initialize x. Since x is of type double, the value is implicitly converted from int to double, resulting in x holding the value 1.0.

If you want the value of x to be 1.8, you need to do floating-point division, which means you need floating-point operands. The simplest and clearest way to do this is:

double x = 9.0 / 5.0;

There are several other (IMHO less clear) approaches. If a / operator has two operands, one of type int and one of type double, the int operand is promoted to double, so either of these will also set x to 1.8:

double x = 9.0 / 5;
/* or */
double x = 9 / 5.0;

But be careful with this approach:

double y = 9 / 5 / 3.0;

This is equivalent to:

double y = (9 / 5) / 3.0;

which computes 9 / 5 as an int, yielding 1, then promotes that result to double and divides it by 3.0, yielding 0.333333333.

The point is that the context of an expression does not impose a type on the expression or its operands; the expression is evaluated as if it were isolated, and then the result may be converted depending on its context.

Carl Norum
  • 219,201
  • 40
  • 422
  • 469
Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
1

I still don't understand why int fahr = celsius / 5 * 9 + 32; works but not int fahr = 9/5 * celsius+32;

For the former, you probably declared celsius as a float or as a double, making the entire right side evaluate as such. Assuming you used float, it works out like this:

celsius / 5 * 9 + 32
(celsius / 5.0) * 9 + 32
(celsius / 5.0 * 9.0) + 32
(celsius / 5.0 * 9.0 + 32.0)

For the latter, 9/5 is integer arithmetic that evaluates to 1 before the rest of the math happens as floating point. In this case:

9 / 5 * celsius + 32
1 * celsius + 32 // Because of this, you get an incorrect answer
celsius + 32
celsius + 32.0

Note the the type of the left hand side is irrelevant; the right-hand side is evaluated without regard to that.

Update: You said celsius is an int, which means you just happened to get lucky and test with a value that is a multiple of 5, giving you a correct integer result to celsius / 5 before doing valid integer arithmetic for the rest of the statement. In your second example, being a multiple of 5 doesn't help you.

In any case, now you know why you got lucky, but the linked question gives you the answer to what you actually need to to do have a formula that works when celsius isn't a multiple of 5: use floating point math, as demonstrated in all of the answers there.

Darshan Rivka Whittle
  • 32,989
  • 7
  • 91
  • 109
  • Nope, celsius is also declared as an int. Someone above mentioned that it's an order of operations thing, but I don't understand why because it's still diving two integers at some point. – Martin Apr 18 '13 at 00:54
  • 2
    Then you got lucky and it was a multiple of `5`, making `celsius / 5` give you a mathematically correct answer. – Darshan Rivka Whittle Apr 18 '13 at 00:55
  • @Tangler: Darshan is right. Try it with a value that's not a multiple of 5; you'll see a truncation error. – Keith Thompson Apr 18 '13 at 00:56
  • @KeithThompson ok thanks, you're right (I don't get an error but I do get an incorrect output). For instance, using celsius 25, 27 or 28 all give 77 fahrenheit. Really not sure why the lecturer used it as the solutuion program then (he did have a loop to only step celsius in increments of 10 then output the conversions as a table - still seems like a bad way to do things though) – Martin Apr 18 '13 at 01:10
  • Thanks your update really addresses the main thing that was confusing me (namely why the other formula was still working), that's why i've marked yours as the accepted answer. – Martin Apr 18 '13 at 01:22
  • You don't need to use floating point - `int fahr = (celsius * 9 + 32*5) / 5;` – Brendan Apr 18 '13 at 02:01
  • @Brendan Sure, if you need or want to avoid floating-point math and don't mind the truncation, that'll get you there. I wouldn't recommend it as a general solution, though. – Darshan Rivka Whittle Apr 18 '13 at 02:35