[comp.lang.c] Re^2: Turbo C 2.0 vs MSC 5.1

raph@tigger.planet.bt.co.uk (Raphael Mankin) (07/28/89)

MSC 'make' has a completely different logic from Unix 'make'. The
differences, though, are not something to go into here.

MSC implements a strange logic in multiplication. If you do something like

	int	i, j;
	long	l;
	...
	l = i*j;

MSC will compute i*j as 32 bits, discard the upper 16 bits and then sign
extend the 16 bits back to 32 bits. If you want to avoid the loss of precision
you have to use a cast to force a 32 by 32 multiplication. e.g.
	l = i * (long)j;

What do other compilers do in the way of preserving or losing
arithmetic precision?.

I a Coral 66 compiler that I wrote some 17 years ago I went to great
lengths to preserve arithmetic precision, including transforming
things like
		a/b/c/d/e
into
		a/(b*c*d*e)
and re-ordering factors so as to do division as late as possible.

exspes@gdr.bath.ac.uk (P E Smee) (08/09/89)

In article <527@tigger.planet.bt.co.uk> raph@tigger.planet.bt.co.uk (Raphael Mankin) writes:
>
>I a Coral 66 compiler that I wrote some 17 years ago I went to great
>lengths to preserve arithmetic precision, including transforming
>things like
>		a/b/c/d/e
>into
>		a/(b*c*d*e)
>and re-ordering factors so as to do division as late as possible.

A bad move, I'd have said.  It is not difficult to think of cases where
'a/b/c/d/e' would give a sensible answer, while 'a/(b*c*d*e)' will
overflow computing the denominator '(b*c*d*e)' and so give nonsense.

Further, the person writing that might have set up condition (or fault,
or signal, take your pick) handlers to catch the hardware conditions that
might result from the repeated division (at a higher level invisible to
the compiler) based on knowledge of likely values/errors.  The reordering
changes the sorts of faults which are likely to occur.

(Having both written and used compilers myself) I'd suggest that it is
generally a bad idea for a compiler to 'rewrite' expressions unless the
rewrite is both mathematically identical in an ideal world, and
pragmatically identical given the range of numbers the machine can
express, and the sorts of faults which will occur with any set of input
values.

(Although, being paranoid, I'd also suggest that the person writing
a/b/c/d/e should inject lots of ()s to indicate that the ordering is
really desired.  I tend to parenthesize everything :-)  Goes without
saying that I strongly feel that languages should honor any order-of-
evaluation requirements which the programmer specifies using parens.

gwyn@smoke.BRL.MIL (Doug Gwyn) (08/10/89)

In article <527@tigger.planet.bt.co.uk> raph@tigger.planet.bt.co.uk (Raphael Mankin) writes:
>	int	i, j;
>	long	l;
>	l = i*j;
>MSC will compute i*j as 32 bits, discard the upper 16 bits and then sign
>extend the 16 bits back to 32 bits.

That's acceptable behavior.  If the operands were unsigned, discarding of
the high bits would even be required behavior.

Dealing with, or better yet avoiding, arithmetic overflow is tricky to do
right.  C explicitly does not constrain the implementation's behavior in
such a case, partly because any simple rule would not be universally
appropriate.

davidsen@sungod.crd.ge.com (ody) (08/11/89)

In article <1989Aug9.094742.20000@gdt.bath.ac.uk> exspes@gdr.bath.ac.uk (P E Smee) writes:

| A bad move, I'd have said.  It is not difficult to think of cases where
| 'a/b/c/d/e' would give a sensible answer, while 'a/(b*c*d*e)' will
| overflow computing the denominator '(b*c*d*e)' and so give nonsense.

  Could you give us an example? Looking at the conditions it sure looks
as though even by avoiding the overflow you would be doing the
equivalent operations and would get an underflow on one of the divides.
I was also able to come up with values which give zero significant
digits without getting a trap.

  I can come up with a few cases where the order of operations was
important (ie big # times fraction times big # doesn't overflow, big #
time big # does, before the fraction). That's a diferent problem, since
order is needed for either multiplication or division.

  I assume that when you said "sensible answer" you meant "correct to
some useful number of digits" rathar than "avoids hardware errors."

	bill davidsen		(davidsen@crdos1.crd.GE.COM)
  {uunet | philabs}!crdgw1!crdos1!davidsen
"Stupidity, like virtue, is its own reward" -me

scjones@sdrc.UUCP (Larry Jones) (08/13/89)

In article <1631@crdgw1.crd.ge.com>, davidsen@sungod.crd.ge.com (ody) writes:
> In article <1989Aug9.094742.20000@gdt.bath.ac.uk> exspes@gdr.bath.ac.uk (P E Smee) writes:
> 
> | A bad move, I'd have said.  It is not difficult to think of cases where
> | 'a/b/c/d/e' would give a sensible answer, while 'a/(b*c*d*e)' will
> | overflow computing the denominator '(b*c*d*e)' and so give nonsense.
> 
>   Could you give us an example? Looking at the conditions it sure looks
> as though even by avoiding the overflow you would be doing the
> equivalent operations and would get an underflow on one of the divides.

For simplicity, lets consider just a/b/c vs. a/(b*c).  If a and b
are equal and very large (like DBL_MAX, for example) and c is
some small integral value (like 2.0), then a/b/c is a quite
reasonable, representable value (0.5), but a/(b*c) causes an
overflow (since 2.0 * DBL_MAX is not representable).
----
Larry Jones                         UUCP: uunet!sdrc!scjones
SDRC                                      scjones@SDRC.UU.NET
2000 Eastman Dr.                    BIX:  ltl
Milford, OH  45150-2789             AT&T: (513) 576-2070
"I have plenty of good sense.  I just choose to ignore it."
-Calvin