Integer promotion and overflow

R

Russell Shaw

Hi,
I'm using gcc-3.4.3 on a linux pc. The ints and long ints are 32 bits
and long long ints are 64 bits.

When i have:

int num=9600;
long long int reg=(long long)800000000000/(5000000*num);

the denominator overflows and gives an incorrect answer: reg=1059


With an extra cast:

long long int reg=(long long)800000000000/((long long)5000000*num);

i get: reg=16


If the numerator is a long long int, should the denominator be
automatically promoted to a long long int?
 
J

jacob navia

Russell said:
Hi,
I'm using gcc-3.4.3 on a linux pc. The ints and long ints are 32 bits
and long long ints are 64 bits.

When i have:

int num=9600;
long long int reg=(long long)800000000000/(5000000*num);

the denominator overflows and gives an incorrect answer: reg=1059


With an extra cast:

long long int reg=(long long)800000000000/((long long)5000000*num);

i get: reg=16


If the numerator is a long long int, should the denominator be
automatically promoted to a long long int?

lcc-win32 gives the same answer as gcc. The problem is
that the denomitar *is* converted to long long. The
compiler does:

long long int reg=(long long)800000000000/(long long)(5000000*num);

Neither gcc nor lcc-win32 realize that the denominator is an
expression, converting each member to the highest rank type.

Maybe there are compilers that clever, but I would not rely
on it, and would add the cast.

jacob
 
E

Eric Sosman

Russell said:
Hi,
I'm using gcc-3.4.3 on a linux pc. The ints and long ints are 32 bits
and long long ints are 64 bits.

When i have:

int num=9600;
long long int reg=(long long)800000000000/(5000000*num);

the denominator overflows and gives an incorrect answer: reg=1059


With an extra cast:

long long int reg=(long long)800000000000/((long long)5000000*num);

i get: reg=16


If the numerator is a long long int, should the denominator be
automatically promoted to a long long int?

Yes. But the denominator (in the first form) is itself
the product of two factors of type `int', so it is calculated
in `int' arithmetic. The product is too large for an `int',
so it overflows and is chopped down to 32 bits (on your system;
C itself doesn't guarantee what happens). Then the already-
damaged value is promoted to `long long' for the division, but
it's too late: the denominator is much smaller than intended,
so the quotient comes out much too large.

In the second form, the 5000000 term is converted to `long
long' by the cast operator. The denominator is then the product
of a `long long' and an `int', so `num' is converted to `long
long' and the multiplication is carried out in `long long'
arithmetic and does not overflow. Then the division takes
place, and everything comes out as expected.

A better way to write this would be to make the constants
`long long' to begin with:

long long int reg = 800000000000LL / (5000000LL * num);
 
C

Chris Croughton

lcc-win32 gives the same answer as gcc. The problem is
that the denomitar *is* converted to long long. The
compiler does:

long long int reg=(long long)800000000000/(long long)(5000000*num);

Neither gcc nor lcc-win32 realize that the denominator is an
expression, converting each member to the highest rank type.

Nor should they, C is specified such that conversions occur only when
they are forced because the types are dissimilar (or when forced with a
cast). For instance:

long long denom = 5000000 * num;

will give the same overflow, because it is specified that 5000000*num is
calculated and only then converted to a long long.
Maybe there are compilers that clever, but I would not rely
on it, and would add the cast.

It would not be 'clever', it would be against the standard. There are
other /languages/ which have the semantics the other way round, so that
expression type is propagated left to right (Algol and Pascal, I
believe, do that), but any C compiler must do the sub-expressions first
and promote the types only when it is necessary because the other
operand is a higher type.

Chris C
 
K

Keith Thompson

Russell Shaw said:
I'm using gcc-3.4.3 on a linux pc. The ints and long ints are 32 bits
and long long ints are 64 bits.

When i have:

int num=9600;
long long int reg=(long long)800000000000/(5000000*num);

the denominator overflows and gives an incorrect answer: reg=1059

With an extra cast:

long long int reg=(long long)800000000000/((long long)5000000*num);

i get: reg=16

If the numerator is a long long int, should the denominator be
automatically promoted to a long long int?

The evaluation of an expression is not affected by the context in
which it appears. Think of expressions, including their
subexpressions, as being evaluated bottom-up, not top-down.

The right operand of the "/" operator is:

(5000000*num)

Each operand of the "*" is of type int, so it's an int-by-int
multiplication yielding an int result. (It overflows, which invokes
undefined behavior, which most likely shows up as discarding the
high-order bits.) That int result then becomes the right operand of
the "/" operator. The left operand of the "/" operator is (long
long)800000000000, which is of type long long, so the right operand is
promoted to long long -- but that promotion (to 64 bits) is done
*after* the 32-bit multiplication.

Since the overflow invokes undefined behavior, I suppose a
sufficiently clever compiler could let the context affect the
evaluation of (5000000*num), so the whole expression yields a
mathematically correct result. But such cleverness doesn't really do
you any favors, it merely masks your error.

(There are languages in which the context of an expression affects its
evaluation. C is not such a language.)

You might prefer to use a suffix on the integer constants rather than
casting them to long long:

long long int reg=800000000000LL/(5000000LL*num);

On the other hand, the cast uses the type name explicitly, so it might
be considered clearer.
 
K

Keith Thompson

Chris Croughton said:
On Sun, 02 Jan 2005 17:26:27 +0100, jacob navia


It would not be 'clever', it would be against the standard.
[...]

As I mentioned in another thread, it wouldn't be against the standard
if it affected the visible behavior of the program only when it
invokes undefined behavior. But such "cleverness" would not be a good
idea, since it would tend to mask errors.
 
K

Keith Thompson

Keith Thompson said:
Chris Croughton said:
On Sun, 02 Jan 2005 17:26:27 +0100, jacob navia


It would not be 'clever', it would be against the standard.
[...]

As I mentioned in another thread, it wouldn't be against the standard
if it affected the visible behavior of the program only when it
invokes undefined behavior. But such "cleverness" would not be a good
idea, since it would tend to mask errors.

Sorry, I meant to say "As I mentioned elsewhere in this thread ...".
 
J

Jack Klein

Chris Croughton said:
On Sun, 02 Jan 2005 17:26:27 +0100, jacob navia


It would not be 'clever', it would be against the standard.
[...]

As I mentioned in another thread, it wouldn't be against the standard
if it affected the visible behavior of the program only when it
invokes undefined behavior. But such "cleverness" would not be a good
idea, since it would tend to mask errors.

True. But if either of the operands in the calculation of the
denominator were unsigned, such 'early promotion' would be
non-conforming.
 
N

Neo

Keith Thompson said:
The evaluation of an expression is not affected by the context in
which it appears. Think of expressions, including their
subexpressions, as being evaluated bottom-up, not top-down.

The right operand of the "/" operator is:

(5000000*num)

Each operand of the "*" is of type int, so it's an int-by-int
multiplication yielding an int result. (It overflows, which invokes
undefined behavior, which most likely shows up as discarding the
high-order bits.) That int result then becomes the right operand of
the "/" operator. The left operand of the "/" operator is (long
long)800000000000, which is of type long long, so the right operand is
promoted to long long -- but that promotion (to 64 bits) is done
*after* the 32-bit multiplication.

Since the overflow invokes undefined behavior, I suppose a
sufficiently clever compiler could let the context affect the
evaluation of (5000000*num), so the whole expression yields a
mathematically correct result. But such cleverness doesn't really do
you any favors, it merely masks your error.

(There are languages in which the context of an expression affects its
evaluation. C is not such a language.)

You might prefer to use a suffix on the integer constants rather than
casting them to long long:

long long int reg=800000000000LL/(5000000LL*num);

Hi Keith,

I used the following form :

int num = 9600;
long long int i = (long long)800000000000/(5000000LL * num);
printf("%ld\n", i);

on my Win XP machine (gcc 3.3.1) gcc flash out the following warning :
longlong.c: In function `main':
longlong.c:7: warning: integer constant is too large for "long" type
output : 16

and to more surprise when compiling the same code on Solaris Workstation
with gcc 3.3 warning was the same as above but the result :
output : 0

WHY IT IS SO??????

Thanks n Regards
-Neo
 
M

Michael Mair

Neo said:
Hi Keith,

I used the following form :

int num = 9600;
long long int i = (long long)800000000000/(5000000LL * num);
printf("%ld\n", i);

on my Win XP machine (gcc 3.3.1) gcc flash out the following warning :
longlong.c: In function `main':
longlong.c:7: warning: integer constant is too large for "long" type
output : 16

and to more surprise when compiling the same code on Solaris Workstation
with gcc 3.3 warning was the same as above but the result :
output : 0

WHY IT IS SO??????

RTFmanpage (printf):

The length modifier l specifies _long_, not _long_long_.
If you want to print a long long, use ll:
printf("%lld\n", i);

Depending on the byte representation of the number in memory, we may
well arrive at 16 (korrekt) or 0, if erroneously reading only a part
of the bytes belonging to the argument.


Cheers
Michael
 
K

Keith Thompson

Jack Klein said:
Chris Croughton said:
On Sun, 02 Jan 2005 17:26:27 +0100, jacob navia
Maybe there are compilers that clever, but I would not rely
on it, and would add the cast.

It would not be 'clever', it would be against the standard.
[...]

As I mentioned in another thread, it wouldn't be against the standard [correction: elsewhere in this thread]
if it affected the visible behavior of the program only when it
invokes undefined behavior. But such "cleverness" would not be a good
idea, since it would tend to mask errors.

True. But if either of the operands in the calculation of the
denominator were unsigned, such 'early promotion' would be
non-conforming.

Agreed (because unsigned overflow doesn't invoke undefined behavior
(which you know, of course, but others reading this may not)).
 
N

Neo

Michael Mair said:
RTFmanpage (printf):

The length modifier l specifies _long_, not _long_long_.
If you want to print a long long, use ll:
printf("%lld\n", i);

Depending on the byte representation of the number in memory, we may
well arrive at 16 (korrekt) or 0, if erroneously reading only a part
of the bytes belonging to the argument.

Michael, thanks for the correction. It should be obviously "%lld\n".
But why the warning??? its still there.
 
K

Keith Thompson

Neo said:
I used the following form :

int num = 9600;
long long int i = (long long)800000000000/(5000000LL * num);
printf("%ld\n", i);

on my Win XP machine (gcc 3.3.1) gcc flash out the following warning :
longlong.c: In function `main':
longlong.c:7: warning: integer constant is too large for "long" type
output : 16

In C99, the type of an unsuffixed decimal integer constant is the
first of int, long int, or long long int in which its value can be
represented.

In C90, there is no type long long int, so the constant is of type
long int (which is too small).

I think gcc is acting as a C90 compiler as far as determining the
types of integer constants is concerned, but is supporting long long
as an extension.

Try invoking gcc with "-std=c99" and/or use 800000000000LL rather than
(long long)800000000000.
and to more surprise when compiling the same code on Solaris Workstation
with gcc 3.3 warning was the same as above but the result :
output : 0

Because you're trying to print a long long value, but you're telling
printf to expect a long ("%ld"). Use "%lld" to print a long long.
 
A

aegis

Eric said:
Yes. But the denominator (in the first form) is itself
the product of two factors of type `int', so it is calculated
in `int' arithmetic.

It would seem that 6.4.4.1#5 says that if 5000000 cannot be
represented by the type int then it would try long int
and if not long int, then long long int.

"The type of an integer constant is the first of the
corresponding list in which its value can be represented."

Atleast, that is how I interpreted it. The table follows with

Suffix Decimal Constant
none int
long int
long long int
hopefully google does not ruin my formatting
 
R

Russell Shaw

Neo said:
....



Michael, thanks for the correction. It should be obviously "%lld\n".
But why the warning??? its still there.

I get that warning in gcc-3.4.3 on linux too.
 
M

Michael Mair

Neo said:
Michael, thanks for the correction. It should be obviously "%lld\n".
But why the warning??? its still there.

I do not know which is "line 7", so I can only guess.
Probably, you do not invoke gcc in C99 mode -- C89/90 does not
know the type long long.
Please post your complete code or try what the compiler does for
gcc -Wall -O -std=c99 -pedantic


Cheers
Michael
 
N

Neo

Keith Thompson said:
In C99, the type of an unsuffixed decimal integer constant is the
first of int, long int, or long long int in which its value can be
represented.

In C90, there is no type long long int, so the constant is of type
long int (which is too small).

I think gcc is acting as a C90 compiler as far as determining the
types of integer constants is concerned, but is supporting long long
as an extension.

Try invoking gcc with "-std=c99" and/or use 800000000000LL rather than
(long long)800000000000.

yeah! thnx.
-Neo
 
K

Keith Thompson

aegis said:
Eric said:
Russell Shaw wrote: [...]
If the numerator is a long long int, should the denominator be
automatically promoted to a long long int?

Yes. But the denominator (in the first form) is itself
the product of two factors of type `int', so it is calculated
in `int' arithmetic.

It would seem that 6.4.4.1#5 says that if 5000000 cannot be
represented by the type int then it would try long int
and if not long int, then long long int.

"The type of an integer constant is the first of the
corresponding list in which its value can be represented."

On the system in question, int and long are both 32 bits, and long
long is 64 bits. Since 5000000 fits in 32 bits, it's of type int.
 
A

aegis

is my interpretation correct though?

for example,

long long foo = 5000000000;

if 5000000000 cannot be represented by the type int
then the compiler sees if it can be represented by type long int
and if not, then it tries long long int.

I tried this with gcc invoking -std=c99 and it seems to initialize
to the correct value but issues a warning. Should it issue
the warning? If my interpretation is correct then I do not see how this
warrants a diagnostic.
 
I

infobahn

aegis said:
is my interpretation correct though?

for example,

long long foo = 5000000000;

if 5000000000 cannot be represented by the type int
then the compiler sees if it can be represented by type long int
and if not, then it tries long long int.

I tried this with gcc invoking -std=c99 and it seems to initialize
to the correct value but issues a warning. Should it issue
the warning? If my interpretation is correct then I do not see how this
warrants a diagnostic.

Conforming C compilers can issue diagnostics for any circumstance
they like. They are /required/ to issue at least one diagnostic
if the translation unit contains any syntax errors or constraint
violations, but the Standard does not stop them issuing diagnostics
in other circumstances. For example, it's perfectly legal for
your compiler to diagnose:

int main(void)
{
return 0;
}

like this:

"Fatal error - missing environment division. Format your hard disk."

This diagnostic is utterly misleading, but perfectly legal. Such a
compiler wouldn't sell very well, but that's a completely different
kettle of fish.
 

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

Forum statistics

Threads
473,768
Messages
2,569,575
Members
45,054
Latest member
LucyCarper

Latest Threads

Top