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
dillon@CORY.BERKELEY.EDU (Matt Dillon) (10/12/87)
Aztec does this correctly by using MULS/MULU (thank the lords!)... I depend on it heavily for my graphics work as well... for speed. Lattice C isn't quite as smart... you need to put in some casts, but it still does it. Unfortunetly, most other compilers do not optimize this. If: long z; short x,y; z = (long)((short)x * (short)y); Doesn't work, there probably is no way to do it without using some assembly. BTW, the above applies to 32 bit compilers. 16 bit compilers will usually barf on it since theoretically the result of the multiplication is a short if the arguments are short and the compiler would then extend it to a long destroying the upper 16 bits. You could try the following for 16 bit compilers, but they have to be *smart*: z = (long)((long)x * (long)y); -Matt P.S. that first (long) cast is not strickly required, but some compilers are very stupid when it comes to checking the result variable type when applying optimization.
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
dillon@CORY.BERKELEY.EDU (Matt Dillon) (10/17/87)
:>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. : This is all moot anyway. I would like to point out that even if you *do* use only char's short's and long's you can still get screwed up. Something like: short a,b; long c; c = a * b; Would yield a different result when run on a 32bit-int vs 16bit-int compiler. You aren't going to see me writing code like: c = (long)a * b with MY 32 bit compiler.... My source would become unreadable if I put in all that garbage to ensure it was compileable with 'any' C compiler. :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: True... but garbage. By the same reasons if I made the above assumptions when writing C, my source would become unreadable. Additionaly, it would take me months rather than weeks to complete large programming projects. This is from EXPERIENCE. There have been several occasions where I've had to write programs that would compile on both my Amiga and on a (ich) PC-XT ... and they took about twice as long to write than if for just one machine. In fact, other problems turn up that have nothing to do with standard C. How many of you assume you can fopen() a file and read it byte for byte with getc()??? Think again. But I DO make this assumption whenever I write programs for my Amiga. As far as I'm concerned, for any software I will ever write for the Amiga, integers are 32 bits, shorts are 16 bits, chars are 8 bits. If I start writing software for a CRAY in C, I would make different assumptions. Depending on the machine, my assumptions may be tight (Amiga, Vax), or loose (Cray). And if I ever have to port any of the stuff, tough on me! (or not so tough as the source would be extremely readable). -Matt
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
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]
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