1. We will be in Read Only mode (no new threads, replies, registration) starting at 9:00 EDT for a number of hours as we migrate the forums to upgraded software.

C++ Programming hiccup

Discussion in 'Programmer's Corner' started by mentaaal, Feb 6, 2008.

1. mentaaal Thread Starter Senior Member

Oct 17, 2005
451
0
Hey guys, I am starting a c++ module in college and we were asked to write a temperature convertion program to convert from celcius to faranheit. The program works in the code i have below. But the strange thing is when i put brackets around 5/9 or 9/5 in the formulae, the result is different and very wrong. My lecturer is stumped as well as myself because the code compiles perfectly and mathematically there should be no change to the formulae at all.

Any suggestions as to what is wrong here?

// this program is designed to convert temperatures between degrees and celcius

#include <iostream>
using namespace std;

int main()
{
float celcius, fahreinheit;
int option;

cout << "\nIf you would like to convert from celcius to fahreinheit, please press 1.\nOtherwise if you would like to convert from fahreinheit to celcius,\nplease press 2\n";
cin >> option;

if (option == 1)
{
cout << "Please enter temperature to convert in celcius.\n";
cin >> celcius;

fahreinheit = celcius * 9 / 5 + 32;
cout << celcius << " degrees celcius is equal to:\n" << fahreinheit << " degrees fahreinheit\n";
}
else if (option == 2)
{
cout << "Please enter temperature to convert in fahreinheit.\n";
cin >> fahreinheit;

celcius = (fahreinheit - 32)*5/9;

cout << fahreinheit << " degrees fahreinheit is equal to:\n" << celcius << " degrees celcius\n";
}

else

cout << "This is not a valid response";

return 0;
}

2. Mark44 Well-Known Member

Nov 26, 2007
626
1
Your instructor is not very familiar with C++ or even C.
The reason you get different behavior with (9/5) versus 9/5 has almost nothing to do with mathematics and almost everything to do with conversions between int expressions and float expressions and the order of operations.

Let's look at this assignment statement-- fahreinheit = celcius * 9 / 5 + 32;
Both fahreinheit and celcius are declared as type float, but the constants are type int.

Before the assignment takes place, celsius * 9 is evaluated. In the process, 9 is promoted to type float to give a float result. After that, (celcius * 9) is divided by 5, and 5 is promoted in the process. Finally, the previous result has 32 added to it, and 32 is promoted to float in the addition. The final result is type float, which is added to fahreinheit. (BTW, I'm having a hard time consistently typing your variable names, which are creative variants for fahrenheit and celsius...)

If you add "brackets" (really parentheses) around 9/5, you are causing the expression 9/5 to be evaluated before the other operations happen. Because both 9 and 5 are int values, the result of the division is ***integer*** division, which produces an integer result. In this case, 9/5 evaluates to 1. Because this value eventually is used in an expression involving a float, 1 (much different from what you expected) is promoted to a float value, and so on, but the damage has been done.

If you want to use parentheses around the fractions, do it like so:
9.0/5.0 - a double expression that is more precise than float
5.0/9 - also a double expression, even though the denominator is an int.

Make sense?
Mark

3. Mark44 Well-Known Member

Nov 26, 2007
626
1
One more comment. If the code compiles, this means you have no syntax errors. IOW, you have used the language correctly and that what you have written can be translated into machine code. That doesn't mean you don't have semantic errors, which are generally a bit harder to find. The compiler was able to translate what you wrote, but doesn't know what you meant. For example you thought, quite reasonably, that 9/5 is the same as 1.8 and that 5/9 is roughly .5556. To the C++ compiler (as well as for C, C#, and Java compilers), 9/5 == 1 and 5/9 == 0. That's why your program produced the weird values when you put the parentheses around those fractions.

I don't know what compiler you're using, but if it has a debugger with it, it's can be very helpful when you're trying to find semantic errors in your program. As you single-step through your program you can watch the values of the variables change, which can help you understand which line of code is causing you problems.
Mark

4. mentaaal Thread Starter Senior Member

Oct 17, 2005
451
0
Hi mark, thank you very much for the very detailed reply. I am only a newcomer to the world of programming and the subtleties of it I havent even been taught yet. Aplogies for the variable names I am ashamed to say that they were misspellings. Ok I understand now how I was coding this wrong however could you please explain to me how, when i put the parenthesis around the 5/9 in both formulae, once the code is compiled and run, I get the following: When i select option 2, And it asks me to enter a temperature to convert, i enter 0 and it outputs a 0??? How is this possible?

Thanks for the help,
Greg

5. Dave Retired Moderator

Nov 17, 2003
6,960
172
I'm not a C++ programmer, so I may be well off, but this is how it seems from the mathematical breakdown and what you describe as your observations:

It looks like the float data-type in C++ cannot accommodate negative numbers, i.e. the lowest number that can be represented by the type float is 0. Therefore when you put in 0 the calculations are as follows:

Code ( (Unknown Language)):
1. For: celcius = (fahreinheit - 32)*5/9;
2.
3. fahreinheit = 0;
4.
5. 0 - 32 = 0 <= Because this is the lowest value that can be represented by type float
6.
7. 0 x (5/9) = 0 <= Therefore you answer is zero.
Therefore you need to change the data type of celcius and fahreinheit to reflect the need for negative numbers.

Like I said, I'm not a C++ programmer so I am basing this assessment purely around logic of what is said, it may be the case that the float data type in C++ can accommodate negative numbers.

Dave

6. Dave Retired Moderator

Nov 17, 2003
6,960
172
As a curiosity, why would that (the bolded bit) be the case?

Dave

7. mentaaal Thread Starter Senior Member

Oct 17, 2005
451
0
Thanks for the reply dave, I think your right... i just checked my course notes there no and well done for a non-programmer your pretty on the ball!

8. AlexR Well-Known Member

Jan 16, 2008
735
55
The bold bit happen because both 9 and 5 are integer values (since they have not been defined as float) and the program performs an integer division. The result of an integer division must always be an integer value, so...
18/9 = 2
17/9 =1
9/9 = 1
5/9 = 0
etc.

9. Dave Retired Moderator

Nov 17, 2003
6,960
172
Should have noticed that I guess I've been working in Matlab too long where the default data type is double and such omissions are forgiven. Thanks.

Dave

10. Mark44 Well-Known Member

Nov 26, 2007
626
1
Not true at all. float and double (and long double, when supported) values can be positive or negative.

11. Mark44 Well-Known Member

Nov 26, 2007
626
1
Let's look at what happens when you enter 2 as the option, and then enter 0 as the fahrenheit temperature:
Code ( (Unknown Language)):
1.  celsius = (fahrenheit - 32) * (5/9);
The first expression in parentheses evaluates to -32.0 (type float).
The second expression in parens evaluates to 0, since 5/9 == 0 using integer division.
The product of -32.0 (as a float) and 0 (int) is 0.0 (type float), which is what is stored in the celsius variable in the assignment operation.

That's why your program produces what it does when you add the parens around the 5/9 fraction.

Mark

12. Mark44 Well-Known Member

Nov 26, 2007
626
1
There are two important lessons that can be gleaned from this:
1) C and the languages derived from it (C++, Java, C#) have two kinds of division: integer division and floating point division. Don't assume that what looks like a simple division expression will evaluate the same way as you were taught in elementary school. The two division operations map to two different opcodes in x86 assembly language. Integer division, which isn't very precise, happens in just a few processor clock cycles. Floating point division, which is very precise, takes many more clock cycles. Each type of division has a legitimate place in computing.

2) Expressions made up of different types (e.g., ints mixed in with floats and/or doubles) follow two sets of rules involving how they are evaluated. One set lists the operator precedence (multiplications occur before additions, and so on). The other set of rules describes how operands in mixed expressions are promoted (expanded to fit longer memory locations) and demoted (shortened to fit smaller memory locations).

It's also important to remember that when you're dealing with floating point numbers, the internal representations of these numbers are only approximations. Most of the time, the approximations are close enough, but occasionally there are some very surprising gotchas.

Here's an example:
Code ( (Unknown Language)):
1.
2. float x = 0.1;
3. float sum = 0.0;
4. int i;
5.
6. for (i = 0; i < 10; i++)
7.   sum += x;
8.
9. if (sum == 1.0)
10.   printf("Correct");
11. else
12.   printf("What went wrong?");
13.
The output of the snippet above might surprise you. You can rewrite the printf() calls using the cout stream if you want to do it that way, but the results will be the same.

Mark

13. mentaaal Thread Starter Senior Member

Oct 17, 2005
451
0
Mark, have you ever considered lecturing? So, when initialising a floating point variable, it should be initiallised like so: float x = 1.0 as opposed to float x = 1 Will this cause an error?

Thanks for the VERY comprehensive and helpful replies!

14. mentaaal Thread Starter Senior Member

Oct 17, 2005
451
0
Ok now you have me mystified! I tried your code snippet and also edited it to cout the value for sum after the for loop addition. It tells me that the value in sum is 1! Then if this is the case, why does it still print out what went wrong? If it is a floating point operation as you say and it is very precise, why is it that the output wouldn't cout something close to one? It spits out exactly 1 and no decimal places whatsoever????

15. Mark44 Well-Known Member

Nov 26, 2007
626
1
Been there, done that. I taught mathematics at a community college near Seattle, WA for 18 years. I also taught a slew of programming languages there for about 10 years, including BASIC, Pascal, Modula-2 (a defunct variant of Pascal), C, and C++.

No error. Either way puts exactly the same value into x, although what happens before x gets set is different in the two cases. In the first case, 1.0 is a double (64 bits) and gets demoted to a float (32 bits) before the assignment happens. In the second case, the 32-bit int value is promoted to a float before the assignment occurs.

Mark

16. Mark44 Well-Known Member

Nov 26, 2007
626
1
Greg,
Floating point operations are precise, but not exact. I'm going to leave you in suspense a little while longer before I tell you what happened, but here's something you can try if you can't wait until I reply. Obviously, the program determined that sum was not equal to 1.0. Try displaying the difference. IOW, add a line something like this in the else branch:
Code ( (Unknown Language)):
1.
2. cout << "Difference is " << sum - 1.0;
3.

17. Dave Retired Moderator

Nov 17, 2003
6,960
172
I don't program in C++ and suggested it as an explanation based on logic; hence why I offered a disclaimer in my post IEEE Standard for Binary Floating-Point Arithmetic (IEEE 754-1985 to the layman) provides for both negative and subnormal floating point numbers. I would expect industry standard languages to implement this and standard, but only by doing do you know for sure.

Dave

18. Mark44 Well-Known Member

Nov 26, 2007
626
1
That you did, Dave. No offense intended.

Most of the C, C++, Java, and C# compilers I've worked with over the past 20+ years have supported this IEEE standard. The only one I can recall that didn't was a C compiler that ran on the Apple II family of computers. It didn't support floating point operations at all.
Mark

19. Dave Retired Moderator

Nov 17, 2003
6,960
172
None taken. Is worth noting this, particularly for C++ and C# compilers. I regularly use C and Java, hence why I am familiar with IEEE 754-1985.

Dave

20. mentaaal Thread Starter Senior Member

Oct 17, 2005
451
0
I understand what you are saying about the sum not being precisely 1 but my question is howcome when i cout the value of sum it prints 1 and not something close to it? Especially considering that in my program i submitted earlier, outputs were accurate to quite a few decimal places.

No wait never mind!
Ah yes i think i get what you are saying now. I tried your suggestion and the difference is: -1.19209e-007 which is a tiny amount and correct me if i am wrong about this but the default precision for a float value is 4 decimal places which is why the sum is rounded to 1.

Excellent!