[comp.lang.c] 32bit = 16bit x 16bit

dca@kesmai.COM (David C. Albrecht) (09/29/87)

I would be interested in some other people's input on this.
One of our latest products where I work is a flight simulator.
We do alot of integer arithmetic.  The original target of the
program was the MAC it has since been ported to the Amiga and
is now being ported to the ST.  Okay, note that these are all
68K machines.  In trying to squeeze more speed out of it on the
Amiga I noticed a good many multiplys were 16x16=32 (which is
supported in hardware on the 68000) while it was currently using
32x32=32 (which is not).  In attempting to get the MULS operation
I made the variables 16bit in size and coded the expression thusly:

((long) (x * y))

It is a bit greasy but it was the only way I could think of to get
the proper result.  On the Amiga using Lattice 3.10 it worked ducky.
Recently we moved the code back to the MAC which is using Counsulair (sp?)
and that also seems to work.  On the ST using Mark Williams we were not
so lucky.  They actually explicitly sign extend the result throwing away
the upper 16bits.  We assumed that they did so because their compiler
was not smart enough to recognize that it already had a long result which
it could just use.  After some communication with them they claimed that
the phrase on pg184 in K&R which says that an expression with two ints
should produce an int (their ints are 16 bits) REQUIRE that they sign
extend or they will fail some of their commercial customers acceptance
suites.  Personally I think they are full of it.

Does anyone know of a commercial test suite that checks to make sure that
y = x * x where x is a 16 bit int and y is long (32 bits)  produces garbage
instead of the proper result?  Seems like just the kind of thing I would
want in my software.  Why have good numbers when I could have garbage?

Seriously, I can understand their compiler being a little simple and not
producing the result I desire because it isn't sharp enough to realise that
the result of a MULS is either 16 bits or 32 bits depending on what you
want.  I find it extremely unlikely that this behavior is required, however.
If someone out there could point out to me the error of my ways and why
an acceptance suite would test this or just relate first hand
experience with a suite that does test this I would be most interested.

David Albrecht

rokicki@rocky.STANFORD.EDU (Tomas Rokicki) (10/12/87)

> the phrase on pg184 in K&R which says that an expression with two ints
> should produce an int (their ints are 16 bits) REQUIRE that they sign
> extend or they will fail some of their commercial customers acceptance
> suites.  Personally I think they are full of it.

Actually, no, they are quite correct.  If sizeof(int)==2, and
int x,y ; and long z; and sizeof(long)==4, then

	z = x * y

should generate

	muls	d1,d0
	ext.l	d0

according to K&R; the result *must be* of type int (2 bytes) and therefore
it must be coerced into a long.  The correct way to do this is

	z = x * (long)y ;

which usually generates

	ext.l	d0
	ext.l	d1 ; both operands must be long
	jsr	.muls

but smart compilers (like Manx 3.4) will generate the equivalent

	muls	d1,d0

because the two are exactly idential.  (Similarly, idiotic compilers
will generate, for

char *p, *q ;
if (*p==*q)

	move.b	(a0),d0
	ext.w	d0
	move.b	(a1),d1
	ext.w	d1
	cmp.w	d0,d1

because theoretically, all operations are performed with at least int
accuracy, but smart compilers will generate

	cmp.b	(a0),(a1)

you would be surprised how many generate the former!  Try it.)

> Does anyone know of a commercial test suite that checks to make sure that
> y = x * x where x is a 16 bit int and y is long (32 bits)  produces garbage
> instead of the proper result?  Seems like just the kind of thing I would
> want in my software.  Why have good numbers when I could have garbage?

The rules for C type coercion are well laid down and fairly unambiguous.
Violating them so this one case works is not a good idea.  Specifically,

	int x, y; long z ;

	z = x * y ; /* is not exactly equivalent to */ z = (long)x * (long)y ;
		    /* but */
	z = x * (long)y ; /* is equivalent to */ z = (long)x * (long)y ;

and the lone muls will do the latter, but not the former, if the compiler
is smart enough.  And it doesn't take a much smarter compiler to handle
x * (long)y correctly.

-tom

ark@alice.UUCP (10/13/87)

In article <141@kesmai.COM>, dca@kesmai.UUCP writes:
> Does anyone know of a commercial test suite that checks to make sure that
> y = x * x where x is a 16 bit int and y is long (32 bits)  produces garbage
> instead of the proper result?

Garbage *IS* the proper result.

If x is a 16-bit number, x*x is also a 16-bit number.
The result of overflow is undefined.

If you want the 32-bit product of x and x on a machine with
32-bit longs, the way to write it is

	(long) x * (long) x

or
	x * (long) x

or

	(long) x * x

while is evaluated as

	((long) x) * x

You can then flame at your compiler vendor for not being smart
enough to generate a single instruction for the expression
(in fact, some compilers are smart enough).

rick@oresoft.UUCP (Rick Lahrson) (10/13/87)

In article <141@kesmai.COM> dca@kesmai.COM (David C. Albrecht) writes:
>                      ... In attempting to get the MULS operation
>I made the variables 16bit in size and coded the expression thusly:
>
>((long) (x * y))
>
>It is a bit greasy but it was the only way I could think of to get
>the proper result.  On the Amiga using Lattice 3.10 it worked ducky.
>Recently we moved the code back to the MAC which is using Counsulair (sp?)
>and that also seems to work.  On the ST using Mark Williams we were not
>so lucky.  They actually explicitly sign extend the result throwing away
>the upper 16bits.  We assumed that they did so because their compiler
>was not smart enough to recognize that it already had a long result which
>it could just use.  After some communication with them they claimed that
>the phrase on pg184 in K&R which says that an expression with two ints
>should produce an int (their ints are 16 bits) REQUIRE that they sign
>extend or they will fail some of their commercial customers acceptance
>suites.  Personally I think they are full of it.

I think it's more of an implementation decision.  C tries to fill two
sometimes conflicting niches.  On the one hand, it lets the programmer
get down to the nitty gritty, by making the machine very accessible.
On the other hand, it needs to be as portable as possible.  But porta-
bility for arithmetic requires that you feign ignorance of the actual
capabilities of the machine you're on, and use only capabilities that
all machines/implementations can be expected to have.

For example, if you write:
	a = ((long) (b * c)) / d;
where a, b, c, and d are ints, on a 68K you might expect arithmetically
correct answers for any values of b and c.  But what if an int were 32
bits?  In that case, a long and an int would be the same size, the
multiplication would not be carried to double precision, and some values
of b and c would cause an overflow.  So for portability reasons, the
"common denomimator" among all implementations on all machines seems to
be that the result of a multiplication (1) will contain no fewer bits than
there are in the larger factor (let's hope!); and (2) will contain no
more bits than there are in the larger factor (because many machines/
implementations simply don't provide that capability).

I don't have a C validation suite to look at, and the above definition
of a likely compromise is just my best guess, but I'll bet the compiler
that holds the precision to the size of the operands is the one that
the validation suite will like.

In my copy of K&R, the referenced phrase on p 184 just happens to be ...
The Bottom Line!  8-)

-- 

Rick Lahrson  ...tektronix!oresoft!rick

Disclaimer:  If I ever speak for anyone but me, I'll warn you in advance.

higgin@cbmvax.UUCP (Paul Higginbottom SALES) (10/14/87)

in article <141@kesmai.COM>, dca@kesmai.COM (David C. Albrecht) says:
> Xref: cbmvax comp.lang.c:4799 comp.sys.atari.st:5522 comp.sys.amiga:9125
> 
> I would be interested in some other people's input on this.
> ...I noticed a good many multiplys were 16x16=32 (which is
> supported in hardware on the 68000) while it was currently using
> 32x32=32 (which is not).  In attempting to get the MULS operation
> I made the variables 16bit in size and coded the expression thusly:
> 
> ((long) (x * y))
> 
> It is a bit greasy but it was the only way I could think of to get
> the proper result.  On the Amiga using Lattice 3.10 it worked ducky.
> Recently we moved the code back to the MAC which is using Counsulair (sp?)
> and that also seems to work.  On the ST using Mark Williams we were not
> so lucky.  They actually explicitly sign extend the result throwing away
> the upper 16bits.  We assumed that they did so because their compiler
> was not smart enough to recognize that it already had a long result which
> it could just use.  After some communication with them they claimed that > the phrase on pg184 in K&R which says that an expression with two ints
> should produce an int (their ints are 16 bits) REQUIRE that they sign
> extend or they will fail some of their commercial customers acceptance
> suites.  Personally I think they are full of it.

Personally (and knowing the guys at MWC), I bet they're right.

Second, your expression '((long) (x * y))' should not do as you say because
the cast is performed AFTER the expression is evaluated, so '(x * y)'
is 16bits x 16bits, which is not supposed to be guaranteed to generate 
32 bits, but instead will generate another int (16 bits), the overflow
having probably been lost, or is at least undefined, and THEN the result
is cast to a long, and to make the sign extension work properly, bit 15
will be extended through bits 16-31.
> 
> Does anyone know of a commercial test suite that checks to make sure that
> y = x * x where x is a 16 bit int and y is long (32 bits)  produces garbage
> instead of the proper result?
		 ^^^^^^

This word is important.  I think the result of an OVERFLOWED 16 bit
quantity is undefined.

> David Albrecht

	Paul Higginbottom

mhatter@pnet02.CTS.COM (Patrick E. Hughes) (10/15/87)

In addition to a small letter I sent to the author of the original message
that started this whole mess, I might suggest one thing if you plan on porting
many C programs: Don't Use Ints

Use char, use long, use float, and since they don't change size you're always
set.  Ints, as I've seen, are consistently inconsistent.

UUCP: {hplabs!hp-sdd!crash, ihnp4!scgvaxd!cadovax}!gryphon!pnet02!mhatter
INET: mhatter@pnet02.CTS.COM

crowl@cs.rochester.edu (Lawrence Crowl) (10/16/87)

In article <1912@gryphon.CTS.COM> mhatter@pnet02.CTS.COM
(Patrick E. Hughes) writes:
>I might suggest one thing if you plan on porting many C programs: Don't Use
>Ints.  Use char, use long, use float, and since they don't change size you're
>always set.  Ints, as I've seen, are consistently inconsistent.

NO!  Char, short, and long do change size.  The only thing you can rely on is
that sizeof char <= sizeof short <= sizeof int <= sizeof long.  If you want
portable programs, DO NOT USE BASIC TYPES.  At the top of the program, define a
set of typedefs from program specific types to the basic types.  For example:

     typedef char autos_t ; /* for number of automobiles owned by a person */
                            /* expected range 0 .. 20 */
     typedef int potatoes_t ; /* for number of potatoes eaten by a city */
                            /* expected range 0 .. 80,000,000 */

Because the size of every program type can be changed in exactly one place in
the source, this approach provides two major advantages.  First, it is portable
from one machine to another, and second, it allows the program to adapt to
changing needs or improper design decisions.
-- 
  Lawrence Crowl		716-275-9499	University of Rochester
		      crowl@cs.rochester.edu	Computer Science Department
...!{allegra,decvax,rutgers}!rochester!crowl	Rochester, New York,  14627

mlandau@bbn.com (Matt Landau) (10/16/87)

In comp.lang.c (<1912@gryphon.CTS.COM>), mhatter@pnet02.CTS.COM (Patrick E. Hughes) 
writes:
>In addition to a small letter I sent to the author of the original message
>that started this whole mess, I might suggest one thing if you plan on porting
>many C programs: Don't Use Ints
>
>Use char, use long, use float, and since they don't change size you're always
>set.  

Don's change size, eh?  You want signed or unsigned chars?  And how many bits
are there in a char anyway?  Eight?  Nine?  Thirteen?  The language doesn't
specify anything about chars, or longs (except that sizeof(long) >= sizeof(int),
which isn't much help), or much of anything else where the size and representation
of arithmetic types are concerned.

If you're concerned about being portable, use typedefs.  I generally try to
typedef signed and unsigned types of 8, 16, and 32 bits, and some kind of
short and long flaoting point types.  For most machines, the integer types
work out to:

	typedef	unsigned char	UINT8;
	typedef	unsigned char	BYTE;		/* I like BYTE, so what? */
	typedef	unsigned short	UINT16;
	typedef	unsigned int	UINT32;
	typedef char		INT8;
	typedef short		INT16;
	typedef int		INT32;

though on Intel-based PC's and other perverse hardware, things change a bit.
Note that these are *minimum* number of bits, not *exact* number of bits;
Anything that requires exact sizes is probably going to need its own set
of application-specific typedefs anyway.

The point is, the code only uses "int" or "char" when the details don't matter
and it's important to get the "best" performance (which one assumes you get by
using the "natural size" integer -- that's what "int" in C *means*).  If it 
matters how many bits something is, using the typedefs insures that the code 
is portable to any machine for which all of the appropriate types can be defined.
--
 Matt Landau			Waiting for a flash of enlightenment
 mlandau@bbn.com			  in all this blood and thunder

gwyn@brl-smoke.ARPA (Doug Gwyn ) (10/19/87)

In article <1912@gryphon.CTS.COM> mhatter@pnet02.CTS.COM (Patrick E. Hughes) writes:
>Don't Use Ints

Nonsense!  Not only can you not avoid (int)s, which are the inherent type
of several things in the language, but (int) is usually the fastest data
type with 16 or more bits in any implementation.  If you just need to hold
a modest count, (int) is the best choice.  Also note that many library
functions require (int) arguments.

andy@cbmvax.UUCP (Andy Finkel) (10/20/87)

In article <3294@sol.ARPA> crowl@cs.rochester.edu (Lawrence Crowl) writes:
>In article <1912@gryphon.CTS.COM> mhatter@pnet02.CTS.COM
>(Patrick E. Hughes) writes:
>>I might suggest one thing if you plan on porting many C programs: Don't Use
>NO!  Char, short, and long do change size.  The only thing you can rely on is
>that sizeof char <= sizeof short <= sizeof int <= sizeof long.  If you want
>portable programs, DO NOT USE BASIC TYPES.  At the top of the program, define a

We at the Banzai Institute always use typedefs :-)

Ours are stored in the include file exec/types.h

To maintain readability, however, the names were a bit a bit boring, ie

LONG, ULONG, WORD, UWORD, BYTE, UBYTE, and VOID.

			andy
-- 
andy finkel		{ihnp4|seismo|allegra}!cbmvax!andy 
Commodore-Amiga, Inc.

"Interfere?  Of course we'll interfere.  Always do what you're best at,
 I always say."

Any expressed opinions are mine; but feel free to share.
I disclaim all responsibilities, all shapes, all sizes, all colors.

jimm@mitsumi.UUCP (Jim Mackraz) (10/20/87)

In article <2545@cbmvax.UUCP> andy@cbmvax.UUCP (Andy Finkel) writes:
)In article <3294@sol.ARPA> crowl@cs.rochester.edu (Lawrence Crowl) writes:
)>In article <1912@gryphon.CTS.COM> mhatter@pnet02.CTS.COM
)>(Patrick E. Hughes) writes:
)>>I might suggest one thing if you plan on porting many C programs: Don't Use
)>NO!  Char, short, and long do change size.  The only thing you can rely on is
)>that sizeof char <= sizeof short <= sizeof int <= sizeof long.  If you want
)>portable programs, DO NOT USE BASIC TYPES.  At the top of the program, define a
)
)We at the Banzai Institute always use typedefs :-)
)Ours are stored in the include file exec/types.h
)To maintain readability, however, the names were a bit a bit boring, ie
)LONG, ULONG, WORD, UWORD, BYTE, UBYTE, and VOID.
)andy finkel		{ihnp4|seismo|allegra}!cbmvax!andy 

Well, one qualification on this business is that sometimes you want
Mr. Compiler to choose its most efficient type.  For example, in Intuition,
there was a large number of cases where SHORT was used in loop counters,
and other places where a small counting number was needed.

There appeared to be explicit sign extension operation frequently applied
to these SHORTs.  When many SHORTS were converted to 'ints', the resulting 
code was significantly smaller (note: significant to people who changed
the name "rawinput" to "ri" because we needed the 6 bytes.  Yea, significant
to people who waited from Jul 31 to Aug 1 for the big build because that
second digit in the date string, multiplied by the number of libraries, ...).

It also broke where a pointer to an 'int' was passed to a program expecting
a pointer to a SHORT.

But *sometimes*, there are *some* reasons for saying 'int' instead of either
LONG or SHORT.

	jimm
-- 
	Jim Mackraz
	Mitsumi Technology, Inc.		408/980-5422
	{amiga,pyramid}!mitsumi!jimm

MAXHAM@RICE.BITNET (Mark Maxham) (10/20/87)

But the 68000 MULS and MULU commands multiply two sixteen bit words
into a 32 bit longword.  Thus they are defined.  Is the question
how C translates from multiplicitive expressions into actual machine
instructions?
     
Mark Maxham      maxham@icsa.rice.edu
     

ewhac@well.UUCP (Leo 'Bols Ewhac' Schwab) (10/21/87)

In article <2545@cbmvax.UUCP> andy@cbmvax.UUCP (Andy Finkel) writes:
>We at the Banzai Institute always use typedefs :-)
>To maintain readability, however, the names were a bit a bit boring, ie
>LONG, ULONG, WORD, UWORD, BYTE, UBYTE, and VOID.
>					    ^^^^
	I think your VOID definition is broken.  In the file
exec/interrupts.h is the structure:

struct Interrupt {
	struct Node	is_Node;
	APTR		is_Data;
	VOID		(*is_Code)();
};

	Then, later on, when I try and do this:

foo ()
{
	extern long	bar();

	interrupt.is_Code	= (VOID) bar;
}

	...my compiler throws up.  Something about an invalid use of the
'void' declaration.  I have Manx 3.4b.

	I know, it's probably my fault, so I don't cast it and live with the
warning message....

_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
Leo L. Schwab -- The Guy in The Cape	ihnp4!ptsfa -\
 \_ -_		Recumbent Bikes:	      dual ---> !{well,unicom}!ewhac
O----^o	      The Only Way To Fly.	      hplabs / (pronounced "AE-wack")
"Although there are technical differences between the quality of images
created on the Amiga and on our system, we feel that viewers could be misled
to believe otherwise, even with your disclaimers to the contrary."
		-- Ralph J. Guggenheim, Pixar

andy@cbmvax.UUCP (Andy Finkel) (10/21/87)

In article <4261@well.UUCP> ewhac@well.UUCP (Leo 'Bols Ewhac' Schwab) writes:
>In article <2545@cbmvax.UUCP> andy@cbmvax.UUCP (Andy Finkel) writes:
>	I think your VOID definition is broken.  In the file
>exec/interrupts.h is the structure:

Interesting...in our exec/types.h file we have a

#define VOID 	void

statement, to put the onus of handling this onto the compiler :-)

typedefing VOID as void caused strangeness with Greenhills C.

Well, something else to look into, I guess.

-- 
andy finkel		{ihnp4|seismo|allegra}!cbmvax!andy 
Commodore-Amiga, Inc.

"Interfere?  Of course we'll interfere.  Always do what you're best at,
 I always say."

Any expressed opinions are mine; but feel free to share.
I disclaim all responsibilities, all shapes, all sizes, all colors.

grr@cbmvax.UUCP (George Robbins) (10/21/87)

In article <4261@well.UUCP> ewhac@well.UUCP (Leo 'Bols Ewhac' Schwab) writes:
> In article <2545@cbmvax.UUCP> andy@cbmvax.UUCP (Andy Finkel) writes:
> >We at the Banzai Institute always use typedefs :-)
> >To maintain readability, however, the names were a bit a bit boring, ie
> >LONG, ULONG, WORD, UWORD, BYTE, UBYTE, and VOID.
> >					    ^^^^
> 	I think your VOID definition is broken.  In the file
> exec/interrupts.h is the structure:
> 
> 	Then, later on, when I try and do this:
... 
> foo ()
> {
> 	extern long	bar();
> 	interrupt.is_Code	= (VOID) bar;
> }
> 
> 	...my compiler throws up.  Something about an invalid use of the
> 'void' declaration.  I have Manx 3.4b.

Wait a minute - there are two void concepts: 1) routines that don't return
anything, 2) pointers to things of indeterminate nature.

In you expample, it looks like you are dealing with a long, rather than
a pointer - try (yeech)     = (VOID *) bar;  or why not simply declare
bar() as   VOID *bar();  if this is the only way you are using it?

Me, I'm just a hardware guy this incarnation...

-- 
George Robbins - now working for,	uucp: {ihnp4|rutgers|allegra}!cbmvax!grr
but no way officially representing	arpa: out to lunch...
Commodore, Engineering Department	fone: 215-431-9255 (only by moonlite)

starner@ihlpg.ATT.COM (Guy Starner) (10/23/87)

In article <4261@well.UUCP> ewhac@well.UUCP (Leo 'Bols Ewhac' Schwab) writes:
>	... Then, later on, when I try and do this:
>	...
> 	interrupt.is_Code	= (VOID) bar;
> 
> 	...my compiler throws up.

Shouldn't this be:

	interrupt.is_code = (VOID (*)()) bar;
	
That is, cast bar to a pointer to a function returning void.

				Guy Starner
				AT&T Bell Laboratories
				...!ihnp4!ixlpn!starner
-- 

				Guy Starner
				IHP 2F-522, 416-7396
				ixlpn!starner

cik@l.cc.purdue.edu (Herman Rubin) (10/24/87)

In article <146MAXHAM@RICE>, MAXHAM@RICE.BITNET (Mark Maxham) writes:
> But the 68000 MULS and MULU commands multiply two sixteen bit words
> into a 32 bit longword.  Thus they are defined.  Is the question
> how C translates from multiplicitive expressions into actual machine
> instructions?
>      
> Mark Maxham      maxham@icsa.rice.edu
>      

A general problem with HLL's is that they do not have the necessary
flexibility to handle situations like this.  I do not know of any language
which has in its operation list an operation to multiply two 16 bit object
and get a 32 bit object. (Note:  I am using the term "object" because what is
called a word, longword, etc., differs from machine to machine.)  On many
machines, this would have to be done by the portable method of converting
the 16 bit objects to 32 bits, but the possessor of a machine which can do
the operation directly should not have to go to such lengths to achieve the
results.  How about the related problem of multiplying two 32 bit objects and
getting a 64 bit object?  Other than inserting assembler instructions, which
may involve such problems as not knowing where the (expletive deleted) compiler
has decided to locate the objects, I see no way to do this in any HLL.  I know
of no way to tell the compiler that here is an operation with a given syntax,
and the compiler should put it in its set of operations.  If the syntax is
different from other syntaxes for that operator, the compiler should perform
the overloading.  It is clear that the languages need more flexibility
and that the gurus who claim that they have the solutions are wrong.
-- 
Herman Rubin, Dept. of Statistics, Purdue Univ., West Lafayette IN47907
Phone: (317)494-6054
hrubin@l.cc.purdue.edu (ARPA or UUCP) or hrubin@purccvm.bitnet

rico@oscvax.UUCP (10/24/87)

In article <4261@well.UUCP> ewhac@well.UUCP (Leo 'Bols Ewhac' Schwab) writes:
>
>	I think your VOID definition is broken.  In the file
>exec/interrupts.h is the structure:
>
>struct Interrupt {
>	struct Node	is_Node;
>	APTR		is_Data;
>	VOID		(*is_Code)();
>};
>
>	Then, later on, when I try and do this:
>
>foo ()
>{
>	extern long	bar();
>
>	interrupt.is_Code	= (VOID) bar;
>}

Sorry Leo, I think you missed it...   it should be

	interrupt.is_Code	= (VOID (*)()) bar;

You want to cast bar into a pointer to a function returning VOID
you *don't* want to cast bar into a VOID.  Casting something to VOID
means "throw it away".  Having thrown it away you can't assign it to
interrupt.is_Code.  The only time you ever (well I can't think of
any other reason right now anyways) want to cast something to VOID
(as opposed to a pointer to VOID or pointer to function returning VOID
 or anything like that) is if you want to discard the return value of
some function.  e.g.

	(VOID)gets(s);    	  /* throw away the return value */
				  /* this keeps lint happy */

		-Rico
-- 
[NSA food: terrorist, cryptography, DES, drugs, CIA, secret, decode]
[CSIS food: supermailbox, tuna, fiberglass coffins, Mirabel, microfiche]
[Cat food: Nine Lives, Cat Chow, Meow Mix, Crave]

firth@sei.cmu.edu (Robert Firth) (10/27/87)

In article <593@l.cc.purdue.edu> cik@l.cc.purdue.edu (Herman Rubin) writes:
[discussion of 16*16=>32]
>A general problem with HLL's is that they do not have the necessary
>flexibility to handle situations like this.  I do not know of any language
>which has in its operation list an operation to multiply two 16 bit object
>and get a 32 bit object.

The language RTL/2 has exactly this feature; it has versions of "*" that
can multiply integers to give long ints or fractions to give long fracs.

A more modern language that recognises this problem is Ada, which has
explicit features for fixed-point multiplication with double-length
result (hidden in RM 3.5.9,3.5.10), and indeed for "rule-of-three"
operations (multiply to double length; divide back to single length).
These are defined in fairly general terms, and so can be mapped onto
a variety of multiple-length machine types, not just 16 and 32.

The systems language BCPL has an extension that provides the rule-of-three
primitive (called MULDIV), but for integers only.

mpl@sfsup.UUCP (M.P.Lindner) (10/27/87)

In article <2565@cbmvax.UUCP>, grr@cbmvax.UUCP writes:
> In article <4261@well.UUCP> ewhac@well.UUCP (Leo 'Bols Ewhac' Schwab) writes:
> > In article <2545@cbmvax.UUCP> andy@cbmvax.UUCP (Andy Finkel) writes:
> > >We at the Banzai Institute always use typedefs :-)
> > >To maintain readability, however, the names were a bit a bit boring, ie
> > >LONG, ULONG, WORD, UWORD, BYTE, UBYTE, and VOID.

Here's the final word on several offshoots to the original article I've been
reading.

1.
	> > 	I think your VOID definition is broken.  In the file
	> > exec/interrupts.h is the structure:
	> > 
	> > 	interrupt.is_Code	= (VOID) bar;

What you *should* be doing is
	interrupt.is_Code = (VOID (*)()) bar;

2. as for the person doing
	int	x;

	(long) (x * x)

What *you* should be doing is
	((long) x * x)
since you can't get back the 16 bits lost after you do the
multiply as a 16 bit quantity.

3. as for the people who say "Never use the basic types", I say the following:
	1. use "char" to mean a character or a byte (guaranteed in K&R)
	2. use short for a small integer
	3. use int where the size doesn't matter
	4. use long where you need a long (ie in interfacing to the
	   OS, etc.)
	5. use pointers with care
	6. use void where you mean void (either the compiler handles it
	   or it doesn't - if it does, great, if it doesn't, try "cc -Dvoid=int"
	   but don't go changint the input language because you have a bad
	   compiler.
	7. NEVER NEVER NEVER! use LONG, ULONG, VOID, etc.
	   if you're using ULONG because you want an unsigned 32 bit integer
	   try using
		typedef unsigned long	uint32;
		typedef long	int32;
	  so someone can try to figure out what bizzare thing you were trying
	  to do.

Mike Lindner
attunix!mpl

crowl@cs.rochester.edu (Lawrence Crowl) (10/27/87)

In article <2262@sfsup.UUCP> mpl@sfsup.UUCP (M.P.Lindner) writes:
>3. as for the people who say "Never use the basic types", I say the following:
>	1. use "char" to mean a character or a byte (guaranteed in K&R)

Use char to mean a character.  Use "typedef char applicationtype" when the best
implementation for the intended use happens to be a char on your machine.  My
six bit char may not give the range you assumed because of your chars are
twelve bits.

>	2. use short for a small integer

Use "typedef short applicationtype" when the best implementation for the
intended use happens to be a short on your machine.  My short may be longer
than your short.  This way I only have to change the implementation in one
place.

>	3. use int where the size doesn't matter

Use "typedef int applicationtype" when the implementation for the intended use
does not matter significantly.  Let's hope my ints are at least as large as
you assume.

>	4. use long where you need a long (ie in interfacing to the OS, etc.)

Use a crowbar where you need a crowbar?  Use "typedef long systemtype" when the
implementation for the system type is a long.  When porting, changing the
types passed to system routines in one place can reduce hassle considerably.

>	5. use pointers with care

Amen.

>	6. use void where you mean void (either the compiler handles it or
>	   it doesn't - if it does, great, if it doesn't, try "cc -Dvoid=int"
>	   but don't go changing the input language because you have a bad
>	   compiler.

Agreed.

>	7. NEVER NEVER NEVER! use LONG, ULONG, VOID, etc.
>	   if you're using ULONG because you want an unsigned 32 bit integer
>	   try using
>		typedef unsigned long	uint32;
>		typedef long	int32;
>	  so someone can try to figure out what bizzare thing you were trying
>	  to do.

Use "typedef BASIC_TYPE APPLICATION_TYPE /* required range is a..b */".  This
documents the program's needs and allows people porting the program to identify
exactly what changes need to be made.

-- 
  Lawrence Crowl		716-275-9499	University of Rochester
		      crowl@cs.rochester.edu	Computer Science Department
...!{allegra,decvax,rutgers}!rochester!crowl	Rochester, New York,  14627

mac3n@babbage.UUCP (10/27/87)

In article <593@l.cc.purdue.edu>, cik@l.cc.purdue.edu (Herman Rubin) writes:
> A general problem with HLL's is that they do not have the necessary
> flexibility to handle situations like this.  I do not know of any language
> which has in its operation list an operation to multiply two 16 bit object
> and get a 32 bit object.

In, e.g., Ada the overloading operators allow one to extend "*" for use
with two "16 bit" objects when the context calls for a "32 bit" object.
Ada is fairly unusual in allowing overload resolution based on context,
a feature that adds considerable complexity to the compiler.

> I know
> of no way to tell the compiler that here is an operation with a given syntax,
> and the compiler should put it in its set of operations.

The usual way to extend the set of operations in a language is with procedures.
Many languages are compatible with machine-language procedures.
Some languages allow inline procedues.  Some even allow inline assembler.

If you really want that much control over the code generated, perhaps you
should write your own compiler.  There are several systems which construct
code generators from a set of instructions and their semantics.  Automatic
code generation generation is still pretty new.

> It is clear that the languages need more flexibility
> and that the gurus who claim that they have the solutions are wrong.

Few Gurus claim that they have the perfect solutions.  Merely adequate.

rat@circle.UUCP (10/28/87)

In a previous message, Herman Rubin, Purdue University, sez: 
>.... I do not know of any language which has in its operation list an
>operation to multiply two 16 bit object and get a 32 bit object. ...
   -- and then goes on to say --
>.. How about the related problem of multiplying two 32 bit objects and
>getting a 64 bit object? ...
 
Forth is has an operator M* (pronounced "M-star" for all you 
"pronunciation" types) which takes two 16-bit values and returns their 
32-bit product. I would conjecture that after the smoke clears on a 
possible 32-bit based Forth standard, that M* could change from a 
16x16=32 to a 32x32=64 bit multiply.
 
No doubt someone will say "Forth, ugh!". All I can say is, pooh on you! 
 
           :-)       
 
       "Off with her head!"
 


--  
::: David Douthitt ::: Madison, Wisc ::: 
uucp mail: ...!uwvax!geowhiz!uwspan!circle!rat
fidonet mail: 121/1

bpendlet@esunix.UUCP (10/28/87)

in article <593@l.cc.purdue.edu>, cik@l.cc.purdue.edu (Herman Rubin) says:
> Xref: esunix comp.lang.c:4825 comp.misc:1454
> 
> In article <146MAXHAM@RICE>, MAXHAM@RICE.BITNET (Mark Maxham) writes:
>> But the 68000 MULS and MULU commands multiply two sixteen bit words
>> into a 32 bit longword.  Thus they are defined.  Is the question
>> how C translates from multiplicitive expressions into actual machine
>> instructions?
>>      
>> Mark Maxham      maxham@icsa.rice.edu
>>      
> 
> A general problem with HLL's is that they do not have the necessary
> flexibility to handle situations like this.  I do not know of any language
> which has in its operation list an operation to multiply two 16 bit object
> and get a 32 bit object. 

Whether or not you consider forth to be a high level language is a matter of
taste. But, in the last version of the forth standard that I read ( quite old
actually ) there was an operator named */ that multiplies two 16 bit ints
giving a 32 bit int and then divides by a 16 bit int to give a 16 bit result.
not quite what you asked for, but very close.

> I know
> of no way to tell the compiler that here is an operation with a given syntax,
                                                                        ^^^^^^
I think the term you are looking for here is "signature", the name of the
operator, plus the number of arguments, the type of the arguments, the order
of the arguments and type of the result.

> and the compiler should put it in its set of operations.  If the syntax is
> different from other syntaxes for that operator, the compiler should perform
> the overloading.  It is clear that the languages need more flexibility
> and that the gurus who claim that they have the solutions are wrong.

Operator overloading has been implemented in many compilers. It is pretty
trivial to implement. I know, I've implemented it. If this is meant as a
criticism of C, well there is a lot to criticise about C. But, one of the 
hallmarks of C is its lack of features, especially syntactic features.
C is rather elegant in its syntactic simplicity.

If you are looking for a modern language with a lot of syntactic features
and a lot of flexibility, try reading a good book on ADA. If you want
flexibility with a minimum of syntose, look a LISP.

Programming language syntax is a cancer. The bigger it is, the harder it
is to live with.

			Bob P.
-- 
Bob Pendleton @ Evans & Sutherland
UUCP Address:  {decvax,ucbvax,ihnp4,allegra}!decwrl!esunix!bpendlet
Alternate:     {ihnp4,seismo}!utah-cs!utah-gr!uplherc!esunix!bpendlet
        I am solely responsible for what I say.

henry@utzoo.UUCP (Henry Spencer) (10/28/87)

> ... I do not know of any language
> which has in its operation list an operation to multiply two 16 bit object
> and get a 32 bit object...

As has been said before, in C on a machine where shorts are 16 bits and longs
32, "(long)shortvar * (long)shortvar" does *exactly* this, assuming that the
compiler is smart enough to notice that it can do the casts as part of the
multiplication.  With the bonus that it works, although more slowly, even
if the compiler doesn't notice.

> ... How about the related problem of multiplying two 32 bit objects and
> getting a 64 bit object? ...

Given suitable data types (some 32-bit-machine C compilers have a "long long"
type for 64-bit integers), the same comment applies.
-- 
PS/2: Yesterday's hardware today.    |  Henry Spencer @ U of Toronto Zoology
OS/2: Yesterday's software tomorrow. | {allegra,ihnp4,decvax,utai}!utzoo!henry

toma@tekgvs.TEK.COM (Tom Almy) (10/28/87)

(I don't have the original article)

In article <593@l.cc.purdue.edu> cik@l.cc.purdue.edu (Herman Rubin) writes:

>A general problem with HLL's is that they do not have the necessary
>flexibility to handle situations like this.  I do not know of any language
>which has in its operation list an operation to multiply two 16 bit object
>and get a 32 bit object.

Forth does.  It also provides a 32 bit by 16 bit divide operation, 
a multiply divide function (a*b/c) combining these two for scaled integer
arithmetic, and a divide function returning both quotient and remainder.

Tom Almy
Tektronix, Inc.
toma@tekgvs.TEK.COM

boykin@custom.UUCP (10/28/87)

> In article <146MAXHAM@RICE>, MAXHAM@RICE.BITNET (Mark Maxham) writes:
> > But the 68000 MULS and MULU commands multiply two sixteen bit words
> > into a 32 bit longword.  Thus they are defined.  Is the question
> > how C translates from multiplicitive expressions into actual machine
> > instructions?
> 
> A general problem with HLL's is that they do not have the necessary
> flexibility to handle situations like this.  I do not know of any language
> which has in its operation list an operation to multiply two 16 bit object
> and get a 32 bit object.

I don't believe this is a problem with the language, but with
the compiler.  The compiler should be able to optimize to use
the instructions available to it.  On most C compilers I've
seen the following will use instructions like the 68000's MULS
rather than a subroutine call:

	long	x;		/* 32-bit value	*/
	short	y, z;		/* 16-bit value	*/

	x = y * z;

While this is implementation dependent, it has worked on most
C compilers I've seen.  Don't blame the language when the
compiler (optimizer) is at fault.
-- 

Joe Boykin
...necntc!custom!boykin

ark@alice.UUCP (10/30/87)

In article <776@custom.UUCP>, boykin@custom.UUCP writes:
> I don't believe this is a problem with the language, but with
> the compiler.  The compiler should be able to optimize to use
> the instructions available to it.  On most C compilers I've
> seen the following will use instructions like the 68000's MULS
> rather than a subroutine call:
  
> 	long	x;		/* 32-bit value	*/
> 	short	y, z;		/* 16-bit value	*/
  
> 	x = y * z;
  
> While this is implementation dependent, it has worked on most
> C compilers I've seen.  Don't blame the language when the
> compiler (optimizer) is at fault.


On a machine with a 16-bit multiply that gives a 16-bit product,
the compiler is completely justified in using that multiply
and then sign-extending the result to 32 bits.  If you have any
thoughts about portability, you should be writing:

	x = (long)y * (long)z;

And yes, I have seen compilers that will translate that to
a single 16x16=32 multiply.

barmar@think.COM (Barry Margolin) (10/30/87)

In article <120@babbage.acc.virginia.edu> mac3n@babbage.acc.virginia.edu (Alex Colvin) writes:
>In article <593@l.cc.purdue.edu>, cik@l.cc.purdue.edu (Herman Rubin) writes:
>> A general problem with HLL's is that they do not have the necessary
>> flexibility to handle situations like this.  I do not know of any language
>> which has in its operation list an operation to multiply two 16 bit object
>> and get a 32 bit object.

PL/I's default is to do just this!  And if you multiply "fixed bin
(12,5)" (12 bits with 5 after the binary point) by "fixed bin (16,3)"
the default result is "fixed bin (28,8)".  Imagine -- a language that
follows the rules for arithmetic that we learned in grade school!  For
cases where you know that the result will not be the default (usually
when one of the operands is a literal, and the automatically-generated
data type isn't appropriate), there is a version of the multiplication
operator that lets you specify the size of the result:

	result = multiply (var, 3, 16, 3);

which means "multiply var by 3 giving a sixteen-bit result with three
bits after the binary point)."

---
Barry Margolin
Thinking Machines Corp.

barmar@think.com
seismo!think!barmar

gwyn@brl-smoke.ARPA (Doug Gwyn ) (10/30/87)

In article <2262@sfsup.UUCP> mpl@sfsup.UUCP (M.P.Lindner) writes:
>3. as for the people who say "Never use the basic types", I say the following:

Mike gave good advice -- heed it.

One additional note:  ANSI C will guarantee minimum sizes for the
basic integer data types.  If you KNOW that all values will fit in
a minimum implementation of the data type, you can use it when
storage space is a significant factor.  Otherwise, (int) is usually
the type that is the fastest; however, it may be only 16 bits long
so if that isn't enough for your application you have no choice but
to use long (guaranteed to be at least 32 bits).  If a long isn't
enough, you'll have to find or develop a multiple-precision package
and pay the speed penalty.