[comp.lang.c] Is this a bug in Turbo C 2.0?

murphy@pur-phy (William J. Murphy) (03/06/90)

Yesterday, I was debugging a program of mine and came across an interesting
difference between GNU C and Turbo C. Here is the buggy code:

long SUM;
short data1, data2, data3, data4, data5;

SUM = data1 + data2 + data3 + data4 + data5;

Here is the problem, when the sum of data? < 32767 it sums correctly on both
GNU C and TC.  Hwen the sum of data? > 32767, TC does the rollover and 
becomes negative while GNU C carries the sum as a long and returns the
correct answer.  Which one is correct? (according to ANSI standards)
Of course the fix is simple, but the rest of the program had problems
that are not so simple to fix under MSDOS.
 
SUM = (long)data1 + (long)data2 + (long)data3 + (long)data4 + (long)data5;

solved the problem.

-- 
 Bill Murphy                          murphy@newton.physics.purdue.edu
Enjoying my Amiga 2000, but holding out for a real computer: The Amiga 3000!!

sar@datcon.UUCP (Simon A Reap) (03/09/90)

In article <3210@pur-phy> murphy@newton.physics.purdue.edu.UUCP
(William J. Murphy) writes:
>long SUM;
>short data1, data2, data3, data4, data5;
>SUM = data1 + data2 + data3 + data4 + data5;
>Here is the problem, when the sum of data? < 32767 it sums correctly on both
>GNU C and TC.  Hwen the sum of data? > 32767, TC does the rollover and 
>becomes negative while GNU C carries the sum as a long and returns the
: 
>SUM = (long)data1 + (long)data2 + (long)data3 + (long)data4 + (long)data5;
>solved the problem.
Microsoft C 5.1 (under OS/2) does the same (wrong) thing.  BTW, you only need
to (long) cast one of the data? to get the right result.
-- 
Enjoy,
yerluvinunclesimon      Opinions are mine - my cat (1CC) has her own ideas
Reach me at sar@datcon.co.uk, or ...!mcvax!ukc!pyrltd!datcon!sar

scjones@sdrc.UUCP (Larry Jones) (03/09/90)

In article <3210@pur-phy>, murphy@pur-phy (William J. Murphy) writes:
> [ should long = short + short; be done with short or long
> arithmetic? ]

Either is acceptable.  ANSI C requires the arithmetic to be done
in short -- if the result overflows, the results are undefined,
thus getting the right answer (by actually doing long arithmetic)
is acceptable.
----
Larry Jones                         UUCP: uunet!sdrc!scjones
SDRC                                      scjones@SDRC.UU.NET
2000 Eastman Dr.                    BIX:  ltl
Milford, OH  45150-2789             AT&T: (513) 576-2070
"You know how Einstein got bad grades as a kid?  Well MINE are even WORSE!"
-Calvin

flee@shire.cs.psu.edu (Felix Lee) (03/09/90)

Re the claim that
	short a, b;
	long tot = a + b;
gets the wrong answer if (a + b > 32767),
>Microsoft C 5.1 (under OS/2) does the same (wrong) thing.

It's not the wrong thing.  The shorts are promoted to ints, not longs,
before the addition is performed.  This can overflow if your int is
shorter than your long, as with many PC compilers.

If you want to be portable and get the correct answer, you should cast
(at least one of) the operands to long before adding:
	long tot = (long) a + b;
--
Felix Lee	flee@shire.cs.psu.edu	*!psuvax1!flee

CMH117@psuvm.psu.edu (Charles Hannum) (03/09/90)

In article <1180@sdrc.UUCP>, scjones@sdrc.UUCP (Larry Jones) says:
>
>In article <3210@pur-phy>, murphy@pur-phy (William J. Murphy) writes:
>> [ should long = short + short; be done with short or long
>> arithmetic? ]
>
>Either is acceptable.  ANSI C requires the arithmetic to be done
>in short -- if the result overflows, the results are undefined,
>thus getting the right answer (by actually doing long arithmetic)
>is acceptable.

But relying on the result to be correct in *unacceptable*.


Virtually,
- Charles Martin Hannum II       "Klein bottle for sale ... inquire within."
    (That's Charles to you!)     "To life immortal!"
  cmh117@psuvm.{bitnet,psu.edu}  "No noozzzz izzz netzzzsnoozzzzz..."
  c9h@psuecl.{bitnet,psu.edu}    "Mem'ry, all alone in the moonlight ..."

tim@nucleus.amd.com (Tim Olson) (03/10/90)

In article <E24hr$1@cs.psu.edu> flee@shire.cs.psu.edu (Felix Lee) writes:
| Re the claim that
| 	short a, b;
| 	long tot = a + b;
| gets the wrong answer if (a + b > 32767),
| >Microsoft C 5.1 (under OS/2) does the same (wrong) thing.
| 
| It's not the wrong thing.  The shorts are promoted to ints, not longs,
| before the addition is performed.  This can overflow if your int is
| shorter than your long, as with many PC compilers.
| 
| If you want to be portable and get the correct answer, you should cast
| (at least one of) the operands to long before adding:
| 	long tot = (long) a + b;

This is correct, but note that in the original poster's case, there
was a large sequence of sums:

	tot = a + b + c + d + e + f;

where tot is a long and the others are shorts.  In this case, it is
not sufficient to cast just one of the operands to a long, because the
order of evaluation of the sums is undefined.  The standard widening
rules apply only to the two operands of an operation, so if we cast
only "a" to be a long, and the compiler chose to evaluate the sums
from right-to-left, then only the last sum would be done with long
operands.


	-- Tim Olson
	Advanced Micro Devices
	(tim@amd.com)

coy@ssc-vax.UUCP (Stephen B Coy) (03/10/90)

In article <60@datcon.UUCP>, sar@datcon.UUCP (Simon A Reap) writes:
> In article <3210@pur-phy> murphy@newton.physics.purdue.edu.UUCP
> (William J. Murphy) writes:
> >long SUM;
> >short data1, data2, data3, data4, data5;
> >SUM = data1 + data2 + data3 + data4 + data5;
> >Here is the problem, when the sum of data? < 32767 it sums correctly on both
> >GNU C and TC.  Hwen the sum of data? > 32767, TC does the rollover and 
> >becomes negative while GNU C carries the sum as a long and returns the
> : 
> >SUM = (long)data1 + (long)data2 + (long)data3 + (long)data4 + (long)data5;
> >solved the problem.
> Microsoft C 5.1 (under OS/2) does the same (wrong) thing.  BTW, you only need
> to (long) cast one of the data? to get the right result.

The Real(tm) cause of this problem lies in the fact that the MS-DOS
compilers default to a 16 bit int while GCC defaults to a 32 bit
int.  As stated in K&R Classic, p.41, during arithmetic operations
"char and short are converted to int".  MSC and TC do their
expression evaluation in 16 bit integers and then convert the result
to long (32-bit) for the assignment.  GCC does its evaluation in 32
bit integers and then does the assignment.  Both are legal and
consistant interpretations of the standard.  Caveat programmer.

> yerluvinunclesimon      Opinions are mine - my cat (1CC) has her own ideas
> Reach me at sar@datcon.co.uk, or ...!mcvax!ukc!pyrltd!datcon!sar

Stephen Coy
uw-beaver!ssc-vax!coy

bright@Data-IO.COM (Walter Bright) (03/13/90)

In article <29446@amdcad.AMD.COM> tim@amd.com (Tim Olson) writes:
<	tot = a + b + c + d + e + f;
<where tot is a long and the others are shorts.  In this case, it is
<not sufficient to cast just one of the operands to a long, because the
<order of evaluation of the sums is undefined.  The standard widening
<rules apply only to the two operands of an operation, so if we cast
<only "a" to be a long, and the compiler chose to evaluate the sums
<from right-to-left, then only the last sum would be done with long
<operands.

Not true. The order of evaluation is undefined, but the *binding* is
defined. The binding is defined to be:
	tot = (((((a + b) + c) + d) + e) + f);
Thus if the expression is written as:
	tot = (long)a + b + c + d + e + f;
it is defined to be equivalent to:
	tot = ((((((long)a + b) + c) + d) + e) + f);
now we apply the standard integral promotions to get:
	tot = ((((((long)a + (long)b) + (long)c) + (long)d) +
		(long)e) + (long)f);
*NOW* the order in which the longs are added can be rearranged, but not
before!

Note also that:
	tot = a + b + c + d + e + (long)f;
is equivalent to:
	tot = (long)(a + b + c + d + e) + (long)f;
so beware.

tim@nucleus.amd.com (Tim Olson) (03/13/90)

In article <2374@dataio.Data-IO.COM> bright@Data-IO.COM (Walter Bright) writes:
| In article <29446@amdcad.AMD.COM> tim@amd.com (Tim Olson) writes:

      [ some incorrect drivel about order of evaluation and casting]

| Not true. The order of evaluation is undefined, but the *binding* is
| defined.

Walter is right.  I had just been looking at some code that relied
upon order-of-evaluation, and had that on the brain... ;-)

	-- Tim Olson
	Advanced Micro Devices
	(tim@amd.com)