Okay, I'm confused as to why this is not working the way I think it
should be. The order of precedence and associativity rules for C dictate
that anything within parentheses is evaluated first (left to right), and
for the purpose of my question, followed by multiplicative operators
[*/%], also from left to right.
Sort of. Leave out the evaluation order stuff and replace it
with something like "grouping," and you'd be closer. Precedence and
associativity tell us which operands belong with which operators
(so in `a*b+c/d' we know that the operands of `*' are `a' and `b',
not `a' and `b+c' or `a' and `b+c/d'), but they don't say anything
about the sequence of steps (so in `a*b+c/d' we don't know whether
the multiplication or the division actually happens first).
So, given this simple program:
#include <stdio.h>
int main( void )
{
float f = 27.0f;
float c1 = ( f - 32 ) * 5 / 9;
float c2 = 5 / 9 * ( f - 32 );
printf( "%f\t%f\t%f\n", f, c1, c2 );
return 0;
}
I would expect c1 and c2 to be equal, given the order of precedence and
associativity rules.
You shouldn't expect that. The steps in the c1 computation
are (keeping in mind that they may occur in a different order, but
that however they occur the result must come out the same)
Promote 32 from int to float
Subtract f-32.0f yielding -5.0f
Promote 5 from int to float
Multiply -5.0f by 5.0f yielding -25.0f
Promote 9 from int to float
Divide -25.0f by 9.0f yielding -2.777778 (approximately)
The promotions mentioned above occur because the expression mixes
numbers of different types. So does the c2 computation, but in this
case the outcome is different:
Divide 5 by 9, yielding 0 (5 and 9 are int, so no fraction)
Promote 32 from int to float
Subtract f-32.0f yielding -5.0f
Promote the quotient 0 from int to float
Multiply 0.0f by -5.0f yielding zero (see below)
However, the output is:
27.000000 -2.777778 -0.000000
Can anyone explain to me (1) why c1 and c2 are different, and (2) how I
end up with a negative 0?
Some C implementations (most, even, but not all) distinguish
between "plus zero" and "minus zero." Although the two values are
equal (when compared with `==', for example), the cognoscenti of
fiddly numerical computation find it useful to keep them apart.
For example, if you divide a smallish number by a very large number
the quotient may be so small that it can't be represented exactly,
and may thus wind up as zero -- but the cognoscenti like to think
that it's not "really" zero, just "approximately" zero. In this
view, it's useful to distinguish `1.0/huge/huge -> +0.0' from
`-1.0/huge/huge -> -0.0': One is "very very small but positive"
while the other is "very very small but negative."
... at any rate, that's the rationale as I understand it; I'm
not one of the aforementioned cognoscenti. On systems that make
this distinction, multiplying something by zero gives you a zero,
of course, but with the sign of the "something." In your c2 case,
the "something" was -5.0f, so multiplying by zero gave minus zero.
Not all systems will act this way; some might have given you a plain
zero -- but all would give you a zero of some variety, because the
original `5 / 9' computation is done according to the rules of int
arithmetic and the promotion to float occurs after the fraction has
already been discarded.