[comp.lang.c] long conversion of short expression.

pdg@chinet.chi.il.us (Paul Guthrie) (07/28/89)

Here is a case where 68000 compilers don't seem to agree. 
In the following code segment, the two shorts multiplied together
exceed the size of a short, but the question is, is the result
of the multiplication really a short to be converted to a
long, or a long already?

        main()
        {

                short x=0x18,y=0x588;
                long z=0x100000;
                printf("%lx\n",z+(x*y));
        }
 
Should the compiler generate
	move -2(fp),d0
	muls -4(fp),d0
	extl d0
	addl -8(fp),d0
or should the extl instruction be left out?  muls has already
generated a long result.  Both the Sun and GNU compilers
do not use the extl, but I have seen other compilers that
would.

-- 
Paul Guthrie
chinet!nsacray!paul

roelof@idca.tds.PHILIPS.nl (R. Vuurboom) (07/28/89)

In article <9092@chinet.chi.il.us> pdg@chinet.chi.il.us (Paul Guthrie) writes:
>Here is a case where 68000 compilers don't seem to agree. 
>In the following code segment, the two shorts multiplied together
>exceed the size of a short, but the question is, is the result
>of the multiplication really a short to be converted to a
>long, or a long already?
>
>        main()
>        {
>
>                short x=0x18,y=0x588;
>                long z=0x100000;
>                printf("%lx\n",z+(x*y));
>        }
> 
Following the "usual arithmetic conversions" (cf. Ansi Draft 3.2, K&R v.2
pp. 197) x and y being shorts will be promoted to ints (not necessarily longs)
and a normal int multiplication will take place. Depending on what the
compiler considers to be natural int size the code below may or may not be
correct.

If the int size is the same as that of long then the
code is not correct since the multiplication with subsequent extl will give 
wrong answers if the result does not fit into a short (your example).

If on the other hand the int size is considered a short then 
the is correct since the extl extends the result of the multiplication 
to the long int size for the addition (again a "usual arithmetic conversion").


>Should the compiler generate
>	move -2(fp),d0
>	muls -4(fp),d0
>	extl d0
>	addl -8(fp),d0
>or should the extl instruction be left out?  

Conclusion: 
If your compiler considers the int size to be the same as that of long 
(normal for motorola compilers) then the generated code is incorrect.

If your compiler considers the natural int size to be the same as that
of short then the generated code can be considered correct. (But
maybe you should get yourself a new compiler :-).
-- 
I don't know what the question means, but the answer is yes...
(overheard on comp.lang.misc)
Roelof Vuurboom  SSP/V3   Philips TDS Apeldoorn, The Netherlands   +31 55 432226
domain: roelof@idca.tds.philips.nl             uucp:  ...!mcvax!philapd!roelof

chris@mimsy.UUCP (Chris Torek) (07/28/89)

In article <9092@chinet.chi.il.us> pdg@chinet.chi.il.us (Paul Guthrie) writes:
>Here is a case where 68000 compilers don't seem to agree. 

Most likely because 68000 compilers do not agree as to whether `int'
is the same as `short' or as `long'.

>In the following code segment, the two shorts multiplied together
>exceed the size of a short, but the question is, is the result
>of the multiplication really a short to be converted to a
>long, or a long already?

Neither.

The result of shortvar*shortvar has type int.  (If one variable is
unsigned short, then according to the pANS, the result type is machine
dependent, although the result value is not, provided it does not
overflow.)  If the result overflows, the value is undefined; a clever
int-is-16-bits-style 68000 compiler may produce the correct value by
noticing that the 68000 `muls' instruction produces a 32 bit result,
and carrying that around even though its result type (int) was supposed
to have been only 16 bits.  This is not required by any C standard,
but is permitted since the result value is undefined.  Of course, if
you stuff the 32-bit value into a 16-bit slot, something has to give;
usually it is the value that breaks, rather than the slot.

The result of (intexpr) + (longexpr) has type long.

Anyway:

>	short x=0x18,y=0x588;
>	long z=0x100000;
>	printf("%lx\n",z+(x*y));

>Should the compiler generate
>	move -2(fp),d0
>	muls -4(fp),d0
>	extl d0
>	addl -8(fp),d0

This sequence is typical of a dumb int-is-16-bits compiler.

>or should the extl instruction be left out?

The sequence without the extl is typical of a smarter compiler
of either flavour.

>Both the Sun and GNU compilers do not use the extl, but I have
>seen other compilers that would.

Sun's cc is a moderatly intelligent int-is-32-bit 680x0 compiler
derived from PCC.  GCC as configured for a 680x0 is a smart
int-is-32-bit compiler.  Both realise that there is no point in doing

	extl	-2(fp),d0
	extl	-4(fp),d1
	jsr	lmult		| or some other name
	addl	-8(fp),d0

when the result of both `extl's will have either 0 or 0xffff in
the top 16 bits, and therefore the result from `muls' will be the
same as that from the `multiply two longs' subroutine.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

gwyn@smoke.BRL.MIL (Doug Gwyn) (07/30/89)

In article <9092@chinet.chi.il.us> pdg@chinet.chi.il.us (Paul Guthrie) writes:
>In the following code segment, the two shorts multiplied together
>exceed the size of a short, but the question is, is the result
>of the multiplication really a short to be converted to a
>long, or a long already?

Arithmetic operators that have the same arithmetic type for their operands
always produce the same type for the result.  If you want the result to be
a different type, convert at least one of the operands via a cast before
applying the arithmetic operator.

md@sco.COM (Michael Davidson) (07/31/89)

In article <9092@chinet.chi.il.us> pdg@chinet.chi.il.us (Paul Guthrie) writes:
>Here is a case where 68000 compilers don't seem to agree. 
>In the following code segment, the two shorts multiplied together
>exceed the size of a short, but the question is, is the result
>of the multiplication really a short to be converted to a
>long, or a long already?
Neither - the two (short) variables are promoted to (int) before the
multiplication, the result of the multiplication is an (int) which
gets promoted to a (long) before the addition is performed.
Of course your compiler can generate any code it likes provided that
it gets the answer which is "correct" according to these rules.
(To see why understanding this particular bit of pedantry is important
consider what happens on machines with 16 bit shorts, 16 bit ints and
32 bit longs)

gwyn@smoke.BRL.MIL (Doug Gwyn) (07/31/89)

In article <869@fiasco.sco.COM> md@sco.COM (Michael Davidson) writes:
>Neither - the two (short) variables are promoted to (int) before the
>multiplication, the result of the multiplication is an (int) ...
>Of course your compiler can generate any code it likes provided that
>it gets the answer which is "correct" according to these rules.

Oops, Michael is correct.  I misstated this in an earlier response.
The "usual arithmetic conversions" are applied to the operands before
the operation occurs, and the result has the common promoted type.
Anyway, the important point is that the programmer needs to use
explicit casts to force the shorts to longs if the result might not
fit in a short.

msb@sq.sq.com (Mark Brader) (08/01/89)

Chris Torek has already answered this at length, but I think it deserves
answering briefly also.

> In the following code segment, the two shorts multiplied together
> exceed the size of a short, but the question is, is the result
> of the multiplication really a short to be converted to a
> long, or a long already?

The short answer is, it's really an int to be converted to a long, but
because the semantics of C on integer overflow are explicitly undefined,
it is legitimate for a compiler to treat it as if it was a long already.

To be safe, if there is any chance that the result of the multiplication
will exceed a magnitude of 32767, and you want a long result, you should
cast at least one of the operands to long.

-- 
Mark Brader, SoftQuad Inc., Toronto, utzoo!sq!msb, msb@sq.com
#define	MSB(type)	(~(((unsigned type)-1)>>1))

This article is in the public domain.

prl@inteloa.intel.com (Philip Lantz) (08/03/89)

In article <18795@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
> The result of shortvar*shortvar has type int.
> ...         If the result overflows, the value is undefined; a clever
> int-is-16-bits-style 68000 compiler may produce the correct value by
> noticing that the 68000 `muls' instruction produces a 32 bit result,
> and carrying that around even though its result type (int) was supposed
> to have been only 16 bits.  This is not required by any C standard,
> but is permitted since the result value is undefined.

I agree that if the multiplication overflows, the result is undefined,
but its type is still an int; if a later conversion to long is
required, isn't the compiler required to perform the 'extl' anyway?
That is, shouldn't the result of a conversion from int to long be in
the numeric range of an int, even if the original int value was
undefined?  Or is the compiler free to do whatever it likes with an
entire expression if any sub-expression overflows?

Philip Lantz

P.S.  Sorry to pick nits, Chris; your articles are without fail the most
accurate and informative in this newsgroup.

chris@mimsy.UUCP (Chris Torek) (08/04/89)

>In article <18795@mimsy.UUCP> I noted that
>>The result of shortvar*shortvar has type int. ... If the result
>>overflows, the value is undefined; a clever int-is-16-bits-style 68000
>>compiler [might omit an extl instruction in a subsequent conversion to
>>long, yeilding the `right' value].  This is not required by any C standard,
>>but is permitted since the result value is undefined.

In article <4750@omepd.UUCP> prl@inteloa.intel.com (Philip Lantz) writes:
>I agree that if the multiplication overflows, the result is undefined,
>but its type is still an int; if a later conversion to long is
>required, isn't the compiler required to perform the 'extl' anyway?
>That is, shouldn't the result of a conversion from int to long be in
>the numeric range of an int, even if the original int value was
>undefined?  Or is the compiler free to do whatever it likes with an
>entire expression if any sub-expression overflows?

The last is more or less true: as long as the compiler always produces
the right answer when asked to do things that are defined (such as `300
* 65 => 19500', which cannot overflow---the pANS requires that `int' be
at least 16 bits), it can do anything it wants when the result is
implementation-defined or undefined.  I like to use ridiculous actions,
such as having the computer explode, or change into a butterfly, as
examples of permitted behaviour.

(Incidentally, you really could rig a computer to explode on arithmetic
overflow, particularly if it has an exception for such events.  But I
have not figured out how to change one into a butterfly.  I keep getting
Luna moths. :-) )
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

boyne@hplvli.HP.COM (Art Boyne) (08/04/89)

chris@mimsy.UUCP (Chris Torek) writes:
> it can do anything it wants when the result is
>implementation-defined or undefined.  I like to use ridiculous actions,
>such as having the computer explode, or change into a butterfly, as
>examples of permitted behaviour.

Or you could do what I actually saw one FORTRAN compiler do one day:
the compiler lost a pointer to its symbol table, and when it tried to sort
the symbol table for the cross reference, it instead sorted itself by opcode.
Made for an interesting core dump.

Art Boyne, boyne@hplvla.hp.com

kdb@chinet.chi.il.us (Karl Botts) (08/10/89)

>I agree that if the multiplication overflows, the result is undefined,
>but its type is still an int; if a later conversion to long is
>required, isn't the compiler required to perform the 'extl' anyway?
>That is, shouldn't the result of a conversion from int to long be in
>the numeric range of an int, even if the original int value was
>undefined?  Or is the compiler free to do whatever it likes with an
>entire expression if any sub-expression overflows?

It most certainly is not -- this would break reams of code, inclouding lots
of mine.  Casting an integral value to an integral value of a know size is
a time-honored and legitimate method of truncation.  For instance:

int i;
i = (char)i;

truncates the value in i to the width of a char.  Note that this is not
necessarily the same as either:

i &= 0xFF;

or

i &= 0x7F;

Also, on many architectures a compiler can figure out that it can do:

i = (char)i;

by fiddling with registers, like by clearing th uuper half of a chameleon-size
register or stuffing the wide value into a char-sized register.  Some might
be able to figure this out for:

i &= 0xFF;

but they couldn't for

i &= 0x7F;

This register operation is liable to be faster than fetching a constant and
doing the bitwise AND.  Hence the truncation cast is not only legal, but
useful -- you can't break it.