tags:

views:

101

answers:

1

In the code below, the value of prod is not 9,000,000; it gets a garbage value. Why do we need num1 and num2 to be of type long?

#include <stdio.h>
int main()
{
    int num1 = 3000, num2 = 3000;
    long int prod = num1 * num2;
    printf("%ld\n", prod);
    return 0;
}
+9  A: 

When num1*num2 is computed, it is stored in an intermediate variable that is of the same type (i.e., an int), which comes up as garbage because it's not big enough. Then, the intermediate variable is stored in a long int, but the computed answer was already turned into garbage.

The solution is to cast one of the arguments of the multiplication.

long int prod = (long int)num1 * num2;

This way, the intermediate computation will use the bigger of the two types, and store it temporarily as a long int.

whooops
Note: overflow (and underflow) with ints causes **Undefined Behaviour**. Avoid at all costs: use `unsigned ints` (no UB, but possible wrong results) or check for overflow **before** it happens.
pmg
@pmg: Well, an unsigned int will double the range over a signed one, but that's not always going to prevent an overflow. Is overflowing an unsigned int "defined"?
Steven Sudit
@Steven: when `unsigned ints` "overflow" they do not cause UB; they wrap around to 0 (they operate `MOD(UINT_MAX + 1)`).
pmg
People around here get maybe a little too breathless about UB. `#include <unistd.h>` (or `#include <windows.h>`, pick yer poison) is just as much UB as signed integer overflow. What matters when you get down to it, is how likely the compiler is to make demons fly out of your nose in any given *case* of UB (much more so for signed integer overflow than for platform-specific header files, at least with modern compilers).
Zack
@Zack: Not really - what matters is what implementations you want to be portable to. With `#include <windows.h>`, it's reasonably obvious - but relying on a particular behaviour for signed integer overflow is a lot more subtle.
caf
@Zack: Including platform-specific headers isn't even close to being the same as UB. There are [differences between](http://stackoverflow.com/questions/2046952/limit-the-confusion-caused-by-undefined-behavior/2047172#2047172) undefined, unspecified, and implementation-defined behavior; but using headers not present in the standard library (including any you write) is most definitely a well-defined feature of the language.
Roger Pate
The undefinedness of signed overflow is not just some theoretical matter or weirdness of legacy non-twos-complement machines. Modern gcc uses the fact that signed overflow is UB to optimize better - it assumes the coder has ensured as a precondition that any signed values used in arithmetic cannot result in overflow.
R..
@Roger: No, I know whereof I speak. If you `#include` a header which you did not write, which is not one of the standard headers listed in section 7.1.2, and which does not trip the constraint violation at the beginning of 6.10.2 (i.e. it *does* "identify a file that the implementation can process"), the behavior is undefined. How could it be otherwise? The contents of a platform-specific header are *not defined* by the C standard.
Zack
@Zack: The standard, to my knowledge, does not distinguish between "3rd party" headers (such as unistd.h, sqlite.h, readline.h, ...) and headers you "personally" wrote. Is there a clause I've missed?
Roger Pate
@pmg: I'd call that a well-defined overflow. The reason the signed version is undefined is simply that the C standard doesn't define how negative numbers are stored. These days, it's almost always two's complement, but it could be one's complement or any other viable scheme.
Steven Sudit
I'm pretty sure there is such a clause but I'm not finding it at the moment. I could argue that it falls under the "by the omission of any explicit definition of behavior" language in 4p2 but that feels like a cop-out. Sorry I can't be more conclusive.
Zack