Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

short division question? ( short ) 0x8000 / ( short ) 0xFFFF = ?

519 views
Skip to first unread message

Clem Taylor

unread,
Aug 3, 1995, 3:00:00 AM8/3/95
to
main ( )
{
short a = 0x8000, b = 0xFFFF, c; /* signed 16 bit values */
int ia, ib, ic; /* signed 32 bit values */

c = a / b;
printf ( "%d = %d / %d\n", c, a, b );

ia = a;
ib = b;
ic = ia / ib;

printf ( "%d = %d / %d\n", ic, ia, ib );
}

Returns: (on most machines)
-32768 = -32768 / -1
32768 = -32768 / -1

However, on one machine that I work on c gets 0x7FFF (32767). So, my
question is does the standard define this behavior? The correct answer is
outside the range of a signed 16 bit number. Which answer is more
correct? 0x7FFF seemed like a better answer (at least it's positive), but
I really need to know if the standard specifies this...

Many thanks,
Clem Taylor

Mark Brader

unread,
Aug 3, 1995, 3:00:00 AM8/3/95
to
Clem Taylor (cta...@stimpy.eecis.udel.edu) writes:
> main ( )
> {
> short a = 0x8000, b = 0xFFFF, c; /* signed 16 bit values */
> int ia, ib, ic; /* signed 32 bit values */

The constants 0x8000 and 0xFFFF have the values 32768 and 65535 respect-
ively, and on a machine where int is 32 bits, their type is int. [See
section 6.1.3.2/3.1.3.2.] If short is 16 bits, the values cannot be
represented in the type; an implementation-defined conversion then occurs.
[6.5.7/3.5.7, 6.3.16.1/3.3.16.1, and 6.2.1.2/3.2.1.2]

In practice this conversion is typically done by copying the 16 low bits,
so that on a 2's complement machine we get -32768 for a and -1 for b,
but I don't believe there's anything in the standard that requires it to
be implemented in any particular way. However, from here on I will assume
that the values *are* -32768 and -1.

> c = a / b;

The values of a and b are automatically promoted back to int [6.2.1.1/
3.2.1.1], which is then the type of the result of the / operator
[6.2.1.5/3.2.1.5]. Since -32768 is exactly divisible by -1, and the
result of 32768 is representable in the type int, the division must
yield this value.

On the assignment to c, the value is converted back to short [6.3.16.1/
3.3.16.1]; since 32768 cannot be represented in a 16-bit short, the result
is again implementation-defined [6.2.1.2/3.2.1.2]. (Note: this is *not*
a case of undefined behavior. Undefined behavior is caused by on overflow
during arithmetic [6.3/3.3], but type conversion is not an arithmetic
operation.) Since we assumed above that 32768 would convert to -32768,
this applies again and so this value is stored in c.

> printf ( "%d = %d / %d\n", c, a, b );

The values of c, a, and b are each promoted to int before printing
[6.2.1.1/3.2.1.1], which leaves their values unchanged. Under the
assumptions already given, the output will indeed be

> -32768 = -32768 / -1

as cited.



> ia = a;
> ib = b;
> ic = ia / ib;
>
> printf ( "%d = %d / %d\n", ic, ia, ib );

Here we are doing everything in ints, so there is no problem of overflow
and we naturally get the cited

> 32768 = -32768 / -1

Fine.

> However, on one machine that I work on c gets 0x7FFF (32767). So, my
> question is does the standard define this behavior?

The two places where the program causes implementation-defined behavior
are in the conversions of the hexadecimal initializers, and of the result
of the divisions, from int to short. If the int value 32768 yields the
the short value -32768 in one place, it should do so in the other [*].
If Clem's statement that "c gets 0x7FFF (32767)" means that b is -1 but
a and c are unequal, so that the first line printed is

32767 = -32768 / -1

, then the implementation is wrong. [*]

However, it would also be technically legitimate for 32768 to be converted
to -32767 in the initialization of c, while 65535 still becomes -1.
(I think the standard is wrong to allow this, but as noted above, it
merely says that the conversion is implementation-defined if the result
does not fit.) In that case, the result of the division would obviously
be 32767, the output line being

32767 = -32767 / -1

Another variation would be likely on a 1's complement machine, where we
would expect 65535 and 32768 to be converted to 0 and -32767 respectively;
this is unquestionably legitimate. Since the division by 0 is not
mathematically valid, the result of / operators is then undefined
behavior [6.3/3.3], so *anything* can happen after that [3.16/1.6] --
including, in theory, printf() displaying values different from those
passed to it.

[*] There is a little bit of looseness in the standard in that it doesn't
say that the implementation-defined conversions must always yield the same
result for the same value. As far as I know, this has not been the subject
of an official ruling, but it seems clear to me that the intent is that it
should -- the consequences otherwise would be ridiculous.
--
Mark Brader, m...@sq.com | "An actual human would feel guilt in this situation."
SoftQuad Inc., Toronto | -- Scott Adams: Dilbert

This article is in the public domain.

Paul Eggert

unread,
Aug 4, 1995, 3:00:00 AM8/4/95
to
m...@sq.sq.com (Mark Brader) writes:

>There is a little bit of looseness in the standard in that it doesn't
>say that the implementation-defined conversions must always yield the same
>result for the same value. As far as I know, this has not been the subject
>of an official ruling, but it seems clear to me that the intent is that it
>should -- the consequences otherwise would be ridiculous.

But what about the common practice of giving programmers run-time (or
compile-time) control over the IEEE floating point rounding mode?
Doesn't that cause implementation-defined conversions to yield
different results for the same value?

Clem Taylor

unread,
Aug 4, 1995, 3:00:00 AM8/4/95
to
In article <1995Aug3.1...@sq.sq.com>, Mark Brader <m...@sq.sq.com> wrote:
>Clem Taylor (cta...@stimpy.eecis.udel.edu) writes:
>> main ( )
>> {
>> short a = 0x8000, b = 0xFFFF, c; /* signed 16 bit values */
>
...

>In practice this conversion is typically done by copying the 16 low bits,
>so that on a 2's complement machine we get -32768 for a and -1 for b,
>but I don't believe there's anything in the standard that requires it to
>be implemented in any particular way. However, from here on I will assume
>that the values *are* -32768 and -1.

I think the example I gave did not completely describe hardware involved.
The machine in question is a 16 bit processor where 0x8000 is -32768 and
0xFFFF is also -1.

On the machine in question, the following program:

main ( )
{
int a = 0x8000, b = 0xFFFF, c; /* ints are 16 bits */
int x = -32768, y = -1, z;

c = a / b;

z = x / y;

printf ( "a=%hd b=%hd c=%hd [a=0x%04hX b=0x%04hX c=0x%04hX]\n",
a, b, c, a, b, c );

printf ( "x=%hd y=%hd z=%hd [x=0x%04hX y=0x%04hX z=0x%04hX]\n",
a, b, c, x, y, z );
}

This returns:
a=-32768 b=-1 c=32767 [a=0x8000 b=0xFFFF c=0x7FFF]
x=-32768 y=-1 z=32767 [x=0x8000 y=0xFFFF z=0x7FFF]

If you run the same program on a 32 bit machine with shorts, you get
a=-32768 b=-1 c=-32768 [a=0x8000 b=0xFFFF c=0x8000]
x=-32768 y=-1 z=-32768 [x=0x8000 y=0xFFFF z=0x8000]

So, on a 16 bit machine, where promotion is not an issue, is this behavior
correct? Does the standard specify this...

Many thanks,
Clem

Mark Brader

unread,
Aug 4, 1995, 3:00:00 AM8/4/95
to
Clem Taylor (cta...@stimpy.eecis.udel.edu) writes:
> main ( )
> {
> int a = 0x8000, b = 0xFFFF, c; /* ints are 16 bits */
> int x = -32768, y = -1, z;

On *this* machine, then, the constants 0x8000 and 0xFFFF have the type
unsigned, but their values are still 32768 and 65535 respectively. The
sections I cited previously still apply: the values are converted in an
implementation-defined manner, but in practice can be expected to yield
-32768 and -1 respectively -- as the printf() seems to confirm.

The expression -32768 has the type long and the value -32768. Assuming
2's complement, the value -32768 *can* be represented in an int, and so
it is converted to int without change [6.2.1.2/3.2.1.2]; a and x are equal,
as are b and y.

> c = a / b;

> z = x / y;

In each of these lines, -32768 is being divided by -1 in the type int.
The mathematical result is 32768, which does not fit in the type. This
constitutes an exception, causing the behavior to be undefined [6.3/3.3].

> So, on a 16 bit machine, where promotion is not an issue, is this behavior
> correct? Does the standard specify this...

*Any* behavior is correct [3.16/1.6].
--
Mark Brader \ "It's not in the slightest bit harder to write Fortran
m...@sq.com \ or Basic programs in C++ or Smalltalk than it is
SoftQuad Inc., Toronto \ to write them in C or Pascal." -- Peter da Silva

This article is in the public domain.

Apologies if you saw it twice -- a local glitch.

Mark Brader

unread,
Aug 4, 1995, 3:00:00 AM8/4/95
to
I (Mark Brader, m...@sq.com) wrote:

> > There is a little bit of looseness in the standard in that it doesn't
> > say that the implementation-defined conversions must always yield the
> > same result for the same value. As far as I know, this has not been
> > the subject of an official ruling, but it seems clear to me that the
> > intent is that it should -- the consequences otherwise would be
> > ridiculous.

I was talking about conversions involving integral types, but Paul Eggert
(egg...@twinsun.com)'s observation:

> But what about the common practice of giving programmers run-time (or
> compile-time) control over the IEEE floating point rounding mode?
> Doesn't that cause implementation-defined conversions to yield
> different results for the same value?

is still relevant since the essence of the matter may be considered to be
just what "implementation-defined" means.

However, a flag used at compile time may be considered as selecting a
different C implementation (similarly, it is often asserted that the
ANSIness of gcc depends on whether the flags "-ansi -pedantic" are used).
So in that case, the conversion is still constant within a particular
implementation.

And ANSI C provides no run-time call to change the IEEE float rounding
mode; so if a programmer uses such a call, the program is not strictly
conforming and we don't have to think about it further.

So Paul's examples do not show that "implementation-defined" can include
behavior dependent upon a flag.
--
Mark Brader | Nature is often much more interesting than we would like her
m...@sq.com | to be. However when we finally do understand something, we
SoftQuad Inc. | strike our foreheads and cry "Of course!", and then marvel at
Toronto | how beautifully simple it was all the time. -- Leigh Palmer

This article is in the public domain.

Rad

unread,
Aug 9, 1995, 3:00:00 AM8/9/95
to
In article <3vtig7$9...@louie.udel.edu>, cta...@stimpy.eecis.udel.edu (Clem Taylor) writes:
>In article <1995Aug3.1...@sq.sq.com>, Mark Brader <m...@sq.sq.com> wrote:
>>Clem Taylor (cta...@stimpy.eecis.udel.edu) writes:
>>> main ( )
>>> {
>>> short a = 0x8000, b = 0xFFFF, c; /* signed 16 bit values */
>>
>....

>>In practice this conversion is typically done by copying the 16 low bits,
>>so that on a 2's complement machine we get -32768 for a and -1 for b,
>>but I don't believe there's anything in the standard that requires it to
>>be implemented in any particular way. However, from here on I will assume
>>that the values *are* -32768 and -1.
>
>I think the example I gave did not completely describe hardware involved.
>The machine in question is a 16 bit processor where 0x8000 is -32768 and
>0xFFFF is also -1.
>
...

>
>So, on a 16 bit machine, where promotion is not an issue, is this behavior
>correct? Does the standard specify this...

The standard specifies signed to/from unsigned integer conversion in 6.2.1.2.

Briefly, the standard states that if any such conversion can happen without
changing the value it is done so. The case you have given (conversion from
unsigned to signed) is implementation-defined. The behavior of your program is
therefor not guaranteed.

(Signed to unsigned conversions are however defined as one would expect with
the 2's complement representation unless narrowing is involved.)


----------------------------------------------------------------------------
Richard Deken Graduate student in electrical engineering
PGP public key available Tennessee Technological University
Internet: rad...@gemini.tntech.edu Cookeville, TN, USA

0 new messages