A trick question

G

Gio

Code:
short i, j;

i = -32768;
j = -i;

printf("%d", j);

As some of you may have guessed by my previous post (about books), I'm
trying to learn C. Well, a friend of mine (who is a programmer) asked me
(who is not a programmer) what the output of this would be, and why. I
had no clue, so I ran it, and it printed -32768. I gave him my best
guess (which was wrong):
1) a short maxes out at 32768, so it has no more room to hold the
sign info. That's it for i, anyway. When you define j as -i, it's
saying "here's some sign info, then look at i", not actually doing
the math as -1 * i.

My second guess was:
2) There are little gremlins in my compiler changing signs of my
numbers around. Darn gremlins!

Okay, so that one was wrong too. :(

I have to say, I'm a little bothered by this. Why does it print -32768?

- Gio
 
I

Ioannis Vranos

Gio said:
Code:
short i, j;

i = -32768;
j = -i;

printf("%d", j);

As some of you may have guessed by my previous post (about books), I'm
trying to learn C. Well, a friend of mine (who is a programmer) asked me
(who is not a programmer) what the output of this would be, and why. I
had no clue, so I ran it, and it printed -32768. I gave him my best
guess (which was wrong):
1) a short maxes out at 32768, so it has no more room to hold the sign
info. That's it for i, anyway. When you define j as -i, it's saying
"here's some sign info, then look at i", not actually doing the math
as -1 * i.

My second guess was:
2) There are little gremlins in my compiler changing signs of my
numbers around. Darn gremlins!

Okay, so that one was wrong too. :(

I have to say, I'm a little bothered by this. Why does it print -32768?


Try the following:

#include <stdio.h>

int main(void)
{
short i, j;

i= -32767;

j= -i;

printf("%d\n", j);

i= i- 1;

j= -i;

printf("%d\n", j);

return 0;
}


Give us what this program outputs to your system, and try to explain us
what is happening on each statement.
 
I

Ioannis Vranos

The code fixed:


Ioannis said:
Gio said:
Code:
short i, j;

i = -32768;
j = -i;

printf("%d", j);

As some of you may have guessed by my previous post (about books), I'm
trying to learn C. Well, a friend of mine (who is a programmer) asked me
(who is not a programmer) what the output of this would be, and why. I
had no clue, so I ran it, and it printed -32768. I gave him my best
guess (which was wrong):
1) a short maxes out at 32768, so it has no more room to hold the sign
info. That's it for i, anyway. When you define j as -i, it's saying
"here's some sign info, then look at i", not actually doing the math
as -1 * i.
My second guess was:
2) There are little gremlins in my compiler changing signs of my
numbers around. Darn gremlins!
Okay, so that one was wrong too. :(

I have to say, I'm a little bothered by this. Why does it print -32768?


Try the following:


#include <stdio.h>

int main(void)
{
short i, j;

i= -32767;

j= -i;

printf("i= %hd\n", i);

printf("j= %hd\n\n", j);


i= i- 1;

j= -i;

printf("i= %hd\n", i);

printf("j= %hd\n", j);


return 0;
}



Give us what this program outputs to your system, and try to explain us
what is happening on each statement.
 
I

Ioannis Vranos

Richard said:
Gio said:
Code:
short i, j;

i = -32768;
j = -i;

printf("%d", j);

As some of you may have guessed by my previous post (about books), I'm
trying to learn C. Well, a friend of mine (who is a programmer) asked me
(who is not a programmer) what the output of this would be, and why.

Let's start off by assuming a simpler case, where i is -1, so j takes the
value 1. Since printf is a variadic function, "the default argument
promotions are performed on trailing arguments" (as the Standard says), so
the short int value of 1 is promoted to an int, which matches %d just
fine. So the problem (and yes, there may be one) is not with the %d.

Okay, so i = -32768, and that's a problem right there, because it's outside
the minimum range for short int, which is -32767 to +32767.
Implementations are free to provide wider ranges, but are not forced to. I
*guess* that your implementation actually supports -32768 to +32767. If
I'm right, then what's happening is that the program is trying to assign
-(-32768), which is +32768, into an object that can't support a value that
high. The result is undefined, and the Standard doesn't say anything at
all about what will happen. So whatever gets printed, count yourself lucky
that you probably didn't catch bubonic plague from this code.

But why -32768?

Okay, bearing in mind that the result is undefined so there doesn't even
have to *be* a reason, let's try to see if we can find one anyway.

Let's reduce the problem to four bits (a *really* short int).

You're doing this:

reallyshort i, j;
i = -8; /* bit pattern 1000, two's complement */
j = -i;

C promotes -8 to an int, and then does a unary minus on it, giving 8.
Assuming 16-bit ints (and the same argument applies to larger ints), this
has a bit pattern of 0000000000001000. If we're going to load this value
into j (which has only four bits), something has to give, and it's the
high bits that go, so j gets the value 1000, which is -8!

Using four bits per reallyshort saved me some typing and counting, but
precisely the same argument applies to 16-bit shorts, the only difference
being that a lot more 0s and 1s are involved.


I think a better approach is to let him think what is happening at each
statement.
 
W

Walter Roberson

Richard Heathfield said:
Okay, so i = -32768, and that's a problem right there, because it's outside
the minimum range for short int, which is -32767 to +32767.
Implementations are free to provide wider ranges, but are not forced to. I
*guess* that your implementation actually supports -32768 to +32767. If
I'm right, then what's happening is that the program is trying to assign
-(-32768), which is +32768, into an object that can't support a value that
high. The result is undefined, and the Standard doesn't say anything at
all about what will happen.

The situation gets even more complicated, due to the fact that
C parses -32768 as the unary minus operation applied to 32678 --
and by previous assumption, 32768 is greater than the maximum
signed short. Due to a chain of reasons, it is certain to all work out
for the case of assigning a literal -32768 to a signed int, but

long i = -2147483648;

is not certain to work even when signed long can store that value;
the code may have to be written as

long i = -2147483647-1;
 
G

Gio

I had to modify the program to be used by my compiler (it's an old one;
I'm doing this on an Apple II)

Code:
#include <stdio.h>

main()
{
     short i, j;

     i = -32767;
     j = -i;

     printf("i = %x\n", i);
     printf("j = %x\n\n", j);

     i = i - 1;
     j = -i;

     printf("i = %x\n", i);
     printf("j = %x\n", j);
}

So, I don't know if the output is what you intended, but here goes:

i = 8001
j = 7fff

i = 8000
j = 8000

- Gio
 
I

Ioannis Vranos

Gio said:
I had to modify the program to be used by my compiler (it's an old one;
I'm doing this on an Apple II)


What compiler and version are you using?



%hd did not work? If it didn't, use %d and use the following code instead:



#include <stdio.h>

int main(void)
{
short i;

i= 32767;

printf("i= %hd\n", i);

i= i+1;

printf("i= %hd\n", i);

i= i+1;

printf("i= %hd\n", i);


return 0;
}



Show us the output of the program and what you think each statement does.
 
G

Gio

Well, I'd like to thank you all for your answers. Not sure that I
understand them, but I shall study them. :)

- Gio
 
G

Gio

What compiler and version are you using?

I am using Aztec-C. Its a version of C that's pretty close to but not
quite ANSI C.
%hd did not work? If it didn't, use %d and use the following code
instead:

Well.. it didn't give me any errors, but it just printed the same values
as %d would.

Output:

i = 32767
i = -32768
i = -32767

What it looks like to me is that it's turning over, like an odometer.

- Gio
 
K

Keith Thompson

CBFalconer said:
Gio said:
Code:
short i, j;

i = -32768;
j = -i;

printf("%d", j);

As some of you may have guessed by my previous post (about books),
I'm trying to learn C. Well, a friend of mine (who is a programmer)
asked me (who is not a programmer) what the output of this would
be, and why. I had no clue, so I ran it, and it printed -32768. I
gave him my best guess (which was wrong):

Because your system is (obviously) using 16 bits for integers.
INT_MAX will obviously be 32767 (see limits.h include file) so the
act of negating -32768 creates an overflow. Now you no longer have
any control over the program, since the result is either undefined
or implementation defined.

Um, no.

By "integers", do you mean type int? Remember that there are several
integer types; "int" is just one of them. Yes, the implementation is
using 16 bits for integers, but (probably) only for integers of types
short and unsigned short.

The statement

i = -32768;

(where i is of type short) will work as long as SHRT_MIN is -32768 or
less (i.e., it can fail only if the range of short is exactly -32767
to +32767). The constant 32768 is of type short, int, or long,
whichever is the first in which it will fit. Negating it yields a
value of the same type, which cannot overflow, so you have an
expression of some signed integral type with the value -32768.

Assigning this to i causes it to be converted to short; this can fail
only if, as mentioned above, short's range is exactly -32767 to
+32767 (which is unusual for modern systems).

Now in this statement:

j = -i;

if short is 16 bits, the the unary "-" will overflow. The resulting
is behavior is undefined. A typical symptom of this undefined
behavior is that the value -32768 is assigned to j (given the typical
2's-complement implementation with no overflow checking).

Now if SHRT_MIN is -32767, then the situation is a bit different, but
that can only occur if the implementation uses 1's-complement or
sign-and-magnitude, or if uses 2's-commplement and reserves -32768 as
a trap represenation (I *think* that's permitted).
If you need portable code and values requiring 32 bits, use a
long. Then the size is guaranteed. Unsigned things also define
the action on 'overflow'.

Right. But you can assume that -32768 will fit in 16 bits *if* you
don't mind giving up portability to non-2's-complement implementations
(which might be a reasonable choice).
 
I

Ioannis Vranos

Gio said:
I am using Aztec-C. Its a version of C that's pretty close to but not
quite ANSI C.


What is the name and version of your operating system? Perhaps we can
help you get a more decent compiler.


Well.. it didn't give me any errors, but it just printed the same values
as %d would.

%hd is the proper format specifier for shorts. %d is for ints, %ld is
for longs. You should use %hd for shorts.


Output:

i = 32767
i = -32768
i = -32767

What it looks like to me is that it's turning over, like an odometer.


Exactly, in your case it wraps around. You reach the maximum positive
value, and then it goes to the least negative value.

In C, it is guaranteed that all unsigned integer types wrap around after
they reach their maximum value, that is after their maximum value, if
you increase their value +1, they get to 0.

For signed integer types like short, the behaviour is undefined. In your
case it happens to wrap around, but it could provide some other trash
value, or something else could happen.


ISO C provides a header file <limits.h> that provides the maximum values
for each integer type. You can check the web for more info on <limits.h>.
 
I

Ioannis Vranos

Added some info:

Ioannis said:
What is the name and version of your operating system? Perhaps we can
help you get a more decent compiler.




%hd is the proper format specifier for shorts. %d is for ints, %ld is
for longs. You should use %hd for shorts.





Exactly, in your case it wraps around. You reach the maximum positive
value, and then it goes to the least negative value.

In C, it is guaranteed that all unsigned integer types wrap around after
they reach their maximum value, that is after their maximum value, if
you increase their value +1, they get to 0.

For signed integer types like short, the behaviour is undefined. In your
case it happens to wrap around, but it could provide some other trash
value, or something else could happen.


ISO C provides a header file <limits.h> that provides the maximum values
==> and minimum values for each integer type. You can check the web for
more info on <limits.h>.
 
B

Bill Buckels

On Apr 20, 7:34 am, Ioannis Vranos > What is the name and version of
your operating system? Perhaps we can
help you get a more decent compiler.

You have never used a more decent compiler since one has never been
written.

Many I think are equal for their respective purposes and Walter
Bright's Digital Mars comes to mind.

Having said that, if you do find a more decent C compiler for the
Apple IIe running ProDOS 8 with better libraries and linking options
and the use of overlays and auxilliary memory, inline assembly, etc.
and documentation let us know. I am sure such a thing would have been
written by odin himself.

I am leaving this link again. For a qualitative and quantitative
evaluation and to define "better compiler" or IMO "best compiler":

http://www.clipshop.ca/Aztec/index.htm

Have some fun if you get the chance<g>
 
B

Bill Buckels

%hd did not work? If it didn't, use %d and use the following code instead:

<snip>

Ioannis, here's the source from the format function in his compiler.
If you are anticipating expected output here is what to expect. The
architecture is 16 bit and addressable to 0xffff. int foo=32768; like
in MS-DOS segmented memory is (signed short) -1

x--- snip ---x

format.c
/* Copyright (C) 1981,1982,1983 by Manx Software Systems */
#include <ctype.h>


#if MPU8080 || MPUZ80
char *_fmtcvt();
#else


static char *
_fmtcvt(ap, base, cp, len)
int *ap; register char *cp;
{
register unsigned long val;
static char digits[]="0123456789abcdef";


if (len == sizeof(long))
val = *(long *)ap;
else if (base > 0)
val = *(unsigned *)ap;
else
val = *ap;


len = 0;
if (base < 0) {
base = -base;
if ((long)val < 0) {
val = -val;
len = 1;
}
}


do {
*--cp = digits[(int)(val%base)];
} while ((val /= base) != 0);
if (len)
*--cp = '-';
return cp;


}


#endif

format(putsub, fmt, argp)
register int (*putsub)(); register char *fmt; char *argp;
{
register int c;
union {
int *ip;
char *cp;
char **cpp;
#ifdef FLOAT
double *dp;
#endif
} args;
int charcount;
int rj, fillc;
int maxwidth, width;
int i, k;
char *cp;
auto char s[200];


charcount = 0;
args.cp = argp;
while ( c = *fmt++ ) {
if ( c == '%' ) {
s[14] = 0;
rj = 1;
fillc = ' ';
maxwidth = 10000;
if ((c = *fmt++) == '-') {
rj = 0;
c = *fmt++;
}
if (c == '0') {
fillc = '0';
c = *fmt++;
}
if (c == '*') {
width = *args.ip++;
c = *fmt++;
} else {
for (width = 0 ; isdigit(c) ; c = *fmt+
+)
width = width*10 + c - '0';
}
if ( c == '.' ) {
if ((c = *fmt++) == '*') {
maxwidth = *args.ip++;
c = *fmt++;
} else {
for (maxwidth = 0 ;
isdigit(c) ; c = *fmt++)
maxwidth = maxwidth*10
+ c - '0';
}
}
i = sizeof(int);
if (c == 'l') {
c = *fmt++;
i = sizeof(long);
} else if (c == 'h')
c = *fmt++;


switch ( c ) {
case 'o':
k = 8;
goto do_conversion;
case 'u':
k = 10;
goto do_conversion;
case 'x':
k = 16;
goto do_conversion;


case 'd':
k = -10;
do_conversion:
cp = _fmtcvt(args.cp, k, s+14, i);
args.cp += i;
break;


case 's':
i = strlen(cp = *args.cpp++);
goto havelen;
#ifdef FLOAT
case 'e':
case 'f':
case 'g':
ftoa(*args.dp++, s, maxwidth==10000?
6:maxwidth, c-'e');
i = strlen(cp = s);
maxwidth = 200;
goto havelen;
#endif


case 'c':
c = *args.ip++;
default:
*(cp = s+13) = c;
break;
}


i = (s+14) - cp;
havelen:
if ( i > maxwidth )
i = maxwidth;


if ( rj ) {
if ((*cp == '-' || *cp == '+') &&
fillc == '0') {
--width;
if ((*putsub)(*cp++) == -1)
return -1;
}
for (; width-- > i ; ++charcount)
if ((*putsub)(fillc) == -1)
return -1;
}
for ( k = 0 ; *cp && k < maxwidth ; ++k )
if ((*putsub)(*cp++) == -1)
return -1;
charcount += k;


if ( !rj ) {
for (; width-- > i ; ++charcount)
if ((*putsub)(' ') == -1)
return -1;
}
} else {
if ((*putsub)(c) == -1)
return -1;
++charcount;
}
}
return charcount;



}


x--- snip ---x
 
P

Peter Nilsson

Richard said:
Gio said:
Code:
short i, j;

i = -32768;
j = -i;

printf("%d", j);
...
Okay, so i = -32768, and that's a problem right there,
because it's outside the minimum range for short int,
which is -32767 to +32767. Implementations are free to
provide wider ranges, but are not forced to. I *guess*
that your implementation actually supports -32768 to
+32767. If I'm right, then what's happening is that
the program is trying to assign -(-32768), which is
+32768, into an object that can't support a value that
high. The result is undefined,

Yes, but not for the reasons you describe. The negation
of the short value -32768 may overflow, if INT_MAX <
32768; that is the UB. If there is no overflow, then the
value on assignment (with implicit conversion to short)
is AFAIK implementation-defined under C89 (3.2.1.2).
It's only (effectively) UB under C99 because an
implementation-defined signal may be raised.
 
P

Peter Nilsson

Walter said:
...
C parses -32768 as the unary minus operation applied to
32678 -- and by previous assumption, 32768 is greater than
the maximum signed short.

But well within the range of long which is what the constant
32768 will be if INT_MAX < 32768. The subsequent negation
will still be a long, though it may not be in the range of
int or short.
Due to a chain of reasons, it is certain to all work out
for the case of assigning a literal -32768 to a signed
int,

Assuming INT_MIN < -32767, which appeared to be the
case on the OP's implementation.

<snip>
 
K

Keith Thompson

Andrey Tarasevich said:
It can't be 'short'. Selection of type for integral constant in C
begins with 'int' and goes up from there. On a system with more than
16-bit 'int', it's 'int' that will be selected.

You're right.
Not necessarily. Type 'short' will not be used directly in value
context. It will be promoted to 'int' first. So, whether the '-'
itself will overflow or not depends on the properties of 'int', not on
the properties of 'short'. And, again, on a system with more than
16-int 'int' it will not overflow.

Right again.
The overflow might take place
during the attempt to assign the result back to 'short'.

Yes. But if so, that's an overflowing conversion, which yields an
implementation-define result or (new in C99) raises an
implementation-defined signal; an overflow on a unary "-" with a
signed operand would invoke undefined behavior.
In other words, if the range of 'int' is wider than the range of
short' on that particular system, the behavior of these two
assignments will be essentially the same: a successful '-' followed by
a potentially overflowing assignment. If the range of 'int' is the
same as the range of short, then the behavior would be different (as
you describe). The point is that it all depends on the properties of
int', not on the properties of 'short'.

Thanks for the corrections!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,431
Messages
2,571,679
Members
48,796
Latest member
Greg L.

Latest Threads

Top