[comp.lang.c] Possible C compiler bug on 8086 machines

alanf@bruce.cs.monash.OZ.AU (Alan Grant Finlay) (01/12/91)

The following program demonstrates a printf that doesn't seem to work
properly when compiled using a variety of C compilers for an 8086 machine.
The program is as follows:
-----------------------------//---------------------------

#include <stdio.h>

main()
{
   int x,y;
   x = 65536/512;
   y = 512;
   printf("This works : %d, %d\n",x,y);
   printf("This doesn't work : %d, %d\n",65536/512,512);
}

----------------------------//------------------------------

And here is a sample output: 

This works : 128, 512
This doesn't work : 128, 0

Does anyone have any idea why?
Is this a problem with my machine or the printf routine?

ekalenda@cup.portal.com (Edward John Kalenda) (01/12/91)

> The following program demonstrates a printf that doesn't seem to work
> properly when compiled using a variety of C compilers for an 8086 machine.
> The program is as follows:
> 
> #include <stdio.h>
> main()
> {
>    int x,y;
>    x = 65536/512;
>    y = 512;
>    printf("This works : %d, %d\n",x,y);
>    printf("This doesn't work : %d, %d\n",65536/512,512);
> }
> And here is a sample output: 
> This works : 128, 512
> This doesn't work : 128, 0
> 
> Does anyone have any idea why?
> Is this a problem with my machine or the printf routine?

The problem is that 65536/512 results in a LONG being pushed on the stack
so the send %d gets the high word of the long, which is zero. The reason
it works in the first case is that the long result is converted to an int
when assigned to the int x. I use MSC 6.0 with /X4 to catch oddities like
this at compile time. Most UNIX programmers run into this when moving from
the 32 bit machines to DOS.

Ed
ekalenda@cup.portal.com

torvalds@cs.Helsinki.FI (Linus Torvalds) (01/12/91)

In article <3577@bruce.cs.monash.OZ.AU> alanf@bruce.cs.monash.OZ.AU (Alan Grant Finlay) writes:
>
>The following program demonstrates a printf that doesn't seem to work
>properly when compiled using a variety of C compilers for an 8086 machine.
>The program is as follows:
>-----------------------------//---------------------------
>
>#include <stdio.h>
>
>main()
>{
>   int x,y;
>   x = 65536/512;
>   y = 512;
>   printf("This works : %d, %d\n",x,y);
>   printf("This doesn't work : %d, %d\n",65536/512,512);
>}
>
>----------------------------//------------------------------
>
>And here is a sample output: 
>
>This works : 128, 512
>This doesn't work : 128, 0
>
>Does anyone have any idea why?
>Is this a problem with my machine or the printf routine?

This is NOT a bug, but a programming error :-). The 65536 is implicitly
converted to a long (ie 65536L), as it doesn't fit into a int on the
8086 (all 8086 c-compilers I've used use 16-bits ints, but on a 32 bit
int machine this works as "expected"). Thus 65536/512 is a LONG, not an
int and is sent to printf as such. The reason 'x=65536/512;printf(..,x,)
works is that 65536/512 is (implicitly) converted to an int by the
assignment into x.

The code works if you change the second printf to:

printf("%d %d",(int) (65536/512),512);	or alternately:
printf("%ld %d",65536/512,512);

NOTE! Please make 65536 EXPLICITLY long by using 65536L, this makes the
code much clearer and portable. (or don't use machines with a 16 bit int
:-)

		Linus "mostly wrong" Torvalds
		torvalds@cs.helsinki.fi

mbryan@netcom.UUCP (Michael Bryan) (01/13/91)

In article <3577@bruce.cs.monash.OZ.AU> alanf@bruce.cs.monash.OZ.AU
 (Alan Grant Finlay) writes:
[Program which gives unexpected results:]
>#include <stdio.h>
>main()
>{
>   int x,y;
>   x = 65536/512;
>   y = 512;
>   printf("This works : %d, %d\n",x,y);
>   printf("This doesn't work : %d, %d\n",65536/512,512);
>}
>And here is a sample output: 
>This works : 128, 512
>This doesn't work : 128, 0

The problem is that in the printf statement, 65536/512 is a long integer,
and gets passed to printf as to 4 bytes, not 2.  Your format control says
expect two 2-byte integers, so the higher-order bytes of the longword
get interpreted as the second integer, and they are zero.  If you changed
your printf control to "%ld, %d", you would see the correct answer.  (Or
perhaps more revealing, change it to "%d, %d, %d", to see "128, 0, 512"
printed.)  Yet another alternative would be to cast the constant before
passing to printf, as in "(int)(65536/512)".

The reason is that the compiler sees 65536 as requiring a longword (max
short integer is 65535, unsigned), so the type of the constant 65536 is
a longword.  The constant 65536/512 is a long divided by a short, which  
will also have a type of long, due to C's type-conversion rules.  The
reason "x = 65536/512" works is that the long constant "128" is converted
back to a short integer before storing the value in "x".  The first call
to printf then passes two bytes for "x", and it prints as expected.

As far as I know, this is completely proper behaviour for C.  I'm not
sure if all 16-bit integer versions of C behave exactly this way, but
any I've seen do.


-- 
Mike Bryan (mbryan@netcom, {claris,apple}!netcom!mbryan)
W:408/733-6565 H:408/738-2479  Sunnyvale, CA
"Does this sentence remind you of Agatha Christie?"

henry@zoo.toronto.edu (Henry Spencer) (01/13/91)

In article <20722@netcom.UUCP> mbryan@netcom.UUCP (Michael Bryan) writes:
>As far as I know, this is completely proper behaviour for C.  I'm not
>sure if all 16-bit integer versions of C behave exactly this way, but
>any I've seen do.

All the correct ones do.  Some of the more cautious ones warn you about it.
-- 
If the Space Shuttle was the answer,   | Henry Spencer at U of Toronto Zoology
what was the question?                 |  henry@zoo.toronto.edu   utzoo!henry

gwyn@smoke.brl.mil (Doug Gwyn) (01/13/91)

In article <3577@bruce.cs.monash.OZ.AU> alanf@bruce.cs.monash.OZ.AU (Alan Grant Finlay) writes:
>   printf("This doesn't work : %d, %d\n",65536/512,512);

Of course it doesn't work, you're trying to print a long int using a %d
format specification!

Ralf.Brown@B.GP.CS.CMU.EDU (01/13/91)

--
{backbone}!cs.cmu.edu!ralf  ARPA: RALF@CS.CMU.EDU   FIDO: Ralf Brown 1:129/3.1
BITnet: RALF%CS.CMU.EDU@CMUCCVMA   AT&Tnet: (412)268-3053 (school)   FAX: ask
DISCLAIMER?  Did  | It isn't what we don't know that gives us trouble, it's
I claim something?| what we know that ain't so.  --Will Rogers

ark@alice.att.com (Andrew Koenig) (01/14/91)

In article <3577@bruce.cs.monash.OZ.AU> alanf@bruce.cs.monash.OZ.AU (Alan Grant Finlay) writes:

>    printf("This doesn't work : %d, %d\n",65536/512,512);

> This doesn't work : 128, 0

> Is this a problem with my machine or the printf routine?

It's probably a bug in your program.

If your compiler stores ints in 16 bits, then 65536 is a long
and 65536/512 is also a long.  You should therefore do one of the
following:

	printf("This should work : %d, %d\n", (int)(65536/512), 512);
	printf("This might also work : %ld, %d\n", 65536/512, 512);
-- 
				--Andrew Koenig
				  ark@europa.att.com

t-kevinj@microsoft.UUCP (Kevin Johnson) (01/15/91)

In article <3577@bruce.cs.monash.OZ.AU> alanf@bruce.cs.monash.OZ.AU (Alan Grant Finlay) writes:
...<some stuff deleted here>...
>   int x,y;
>   x = 65536/512;
>   y = 512;
>   printf("This works : %d, %d\n",x,y);
>   printf("This doesn't work : %d, %d\n",65536/512,512);
...<more stuff deleted here>...
>And here is a sample output: 
>This works : 128, 512
>This doesn't work : 128, 0

In this case, 65536/512 is going to be considered a long value (remember
that an int is 16 bits and a long is 32 on the 8086).  In the first case 
(x = 65536/512 ;), the long is being typecast back to an int.  

In the printf statement, no typecasting is being done so the compiler
is pushing a long rather than an int.  When printf is trying to figure
out what you want to print, it grabs the low word of (65536/512) to
fit the first %d, and the high word (zero, in this case) to fit the
second %d.  The last word pushed (OK, technically the first one, but
that's irrelevant here) is the value 512, which is ignored by printf.

To fix this, typecast the 65536/512 in the printf to an int:
  printf ("This doesn't work : %d, %d\n", (int) 65536 / 512, 512) ;

Things will magically work...


--Kevin

cur022%cluster@ukc.ac.uk (Bob Eager) (01/15/91)

In article <3577@bruce.cs.monash.OZ.AU>, alanf@bruce.cs.monash.OZ.AU (Alan Grant Finlay) writes:
> The following program demonstrates a printf that doesn't seem to work
> properly when compiled using a variety of C compilers for an 8086 machine.
> The program is as follows:
> -----------------------------//---------------------------
> 
> #include <stdio.h>
> 
> main()
> {
>    int x,y;
>    x = 65536/512;
>    y = 512;
>    printf("This works : %d, %d\n",x,y);
>    printf("This doesn't work : %d, %d\n",65536/512,512);
> }
> 
> ----------------------------//------------------------------
> 
> And here is a sample output: 
> 
> This works : 128, 512
> This doesn't work : 128, 0
> 

The compiler sees the 65536 in the second printf, and says: "This can't be
represented in an int, I need a long". I wonder if the 'rules' say that
it has to do this; certainly for an expression involving variables, the
presence of a long in an expression coerces the whole expression to long.

Given the above, it is easy to see what is happening. A four byte item is
pushed on the stack when printf is called: this represents 6655536/512 as a
long. The lower two bytes (the first ones on an 80x86) will contain the 128,
and are processed correctly. The second two bytes will be zero, and are
printed as the second value.

I bet if you change the first %d to %ld in the second printf, the expected
answer will appear!
-------------------------+-------------------------------------------------
Bob Eager                | University of Kent at Canterbury
                         | +44 227 764000 ext 7589
-------------------------+-------------------------------------------------

bytehead@bluemoon.uucp (Bryan Price) (02/09/91)

alanf@bruce.cs.monash.OZ.AU (Alan Grant Finlay) writes:

> 
> -----------------------------//---------------------------
> 
> #include <stdio.h>
> 
> main()
> {
>    int x,y;
>    x = 65536/512;
   // You just assigned a long int to a short int here
>    y = 512;
>    printf("This works : %d, %d\n",x,y);
>    printf("This doesn't work : %d, %d\n",65536/512,512);
   // Buzzzzt!  The first %d should be a %ld, since you are
   // passing a long int (not a short int) for the 1st numeric
   // parameter.  65536 is a long, and forces the compiler to
   // pass it as a long, even if it fits in a short int.
> }
> 
> ----------------------------//------------------------------
> 
> And here is a sample output: 
> 
> This works : 128, 512
> This doesn't work : 128, 0
> 
> Does anyone have any idea why?
> Is this a problem with my machine or the printf routine?

This is a problem with your mind.

#include <.sig>