'Unsigned decimal'

B

BartC

I have this code:

int a = -2147483648;
long long int b = 2147483648;

And gcc gives me this warning for each:

"warning: this decimal constant is unsigned only in ISO C90"

These types are signed 32 and 64 bits respectively. How to get rid of this
warning? What is it's problem anyway, in the second example?

(For the first, I guess that 2147483648 is out of range before the negation
is applied.)
 
L

Les Cargill

BartC said:
I have this code:

int a = -2147483648;
long long int b = 2147483648;

And gcc gives me this warning for each:

"warning: this decimal constant is unsigned only in ISO C90"

These types are signed 32 and 64 bits respectively. How to get rid of
this warning? What is it's problem anyway, in the second example?

(For the first, I guess that 2147483648 is out of range before the
negation is applied.)

Nope. -2147483648 is a perfectly valid ( although somewhat
ambiguous ) 32 bit value ( assuming long is 32 bits for
what you are doing ). It is 0x8000000. So is 2147483648.

0x80000000 is never ambiguous.

I would use hex:

C:\c\usenet>cat long.c

//signed long int a = (signed int)-2147483648L;
//signed long long int b = (signed int)2147483648LL;

signed long int a = 0x80000000L;
signed long long int b = 0x80000000LL;
int main(void)
{


printf("a=%ld\n",a);
printf("b=%lld\n",b);

printf("a=%08lx\n",a);
printf("b=%08llx\n",b);
return 0;
}

Output:

C:\c\usenet>a
a=-2147483648
b=2147483648
a=80000000
b=80000000
 
B

Ben Pfaff

BartC said:
I have this code:

int a = -2147483648;
long long int b = 2147483648;

And gcc gives me this warning for each:

"warning: this decimal constant is unsigned only in ISO C90"

These types are signed 32 and 64 bits respectively. How to get rid of
this warning? What is it's problem anyway, in the second example?

One approach is to write:

int a = -2147483647 - 1;
long long int b = 2147483648LL;
 
J

James Kuyper

I have this code:

int a = -2147483648;
long long int b = 2147483648;

And gcc gives me this warning for each:

"warning: this decimal constant is unsigned only in ISO C90"

These types are signed 32 and 64 bits respectively. How to get rid of this
warning? What is it's problem anyway, in the second example?

I suspect you're using a system where INT_MAX==LONG_MAX==214783647;
that's the case on my system, and I can reproduce your warning message
when compiling for C90. According to the C99 rules, if 214783648 >
LONG_MAX, then 214783648 has the type long long int. In C90, that was
not an option, so I would guess that 214783648 had the type 'unsigned int'.
Because unary minus is a operator, and not part of the integer literal
syntax, you can't actually express INT_MIN as integer literal of type
'int', when 'int' is a 2's complement type. The closest you can come is
(-21478367-1).

In the second case, it's simpler. Just apply the LL suffix to force
interpretation as a long long integer literal.
 
J

James Kuyper

Nope. -2147483648 is a perfectly valid ( although somewhat
ambiguous ) 32 bit value ( assuming long is 32 bits for
what you are doing ). It is 0x8000000. So is 2147483648.

In C90, you'd be correct - which just means that it's out of range for
'int' both before, and after, the unary minus is evaluated.

In C99 or later, it has type long long, which he's told us is a 64-bit
type on his system.
 
K

Keith Thompson

Les Cargill said:
Nope. -2147483648 is a perfectly valid ( although somewhat
ambiguous ) 32 bit value ( assuming long is 32 bits for
what you are doing ). It is 0x8000000. So is 2147483648.

0x80000000 is never ambiguous.

Not quite.

In C99 and later, a decimal constant's type is the first of:

int, long int, long long int

in which it fits. In C90 (before long long was invented), the
sequence takes a left turn into unsigned land:

int, long int, unsigned long int

-2147483648 is not a constant; it's a constant expression consisting
of a unary "-" applied to the contant 2147483648.

Assuming int and long are 32 bits and long long is 64 bits (if it
exists), in C90 the constant 2147483648 is of type unsigned long.
Applying unary "-" to it yields an unsigned long result with
the same value. Initializing the int object "a" with that value
causes it to be converted from unsigned long to (signed) int --
which, since 2147483648L is outside the range of int, yields an
implementation-defined value. That value is very likely to be
-2147483648, which is what was intended.

In C99 and later the constant 2147483648 is of type long long, since
it won't fit in a long. Applying unary "-" gives you an expression
of type long long with the value -2147483648. Converting that
value to int unambiguously yields the int value -2147483648.

So as of C99, the declaration

int a = -2147483648;

is unambiguous and initializes a to the obvious value; in C90, it
*probably* does so.

The rules for octal and hexadecimal constants are a bit different. In
C90, the sequence is:

int, unsigned int, long int, unsigned long int

and in C99 and later, its:

int, unsigned int, long int, unsigned long int, long long int,
unsigned long long int

So changing the declaration to:

int a = -0x80000000

doesn't help for C90 (though it may remove the warning); the constant
is of type unsigned int, and the conversion is implementation-defined
(but very likely to yield the result you want). And in C99 and
later, it actually introduces an ambiguity, since -0x80000000 is of
type unsigned int, whereas -2147483648 is of type (signed) long long.

The warning, I think, refers just to the constant 2147483648,
without regard to its context.

If you're willing to assume that int is 32 bits (more specifically,
that INT_MIN == -2147483648 (mathematically)), then you can just
write:

int a = INT_MIN;

If not, you can write:

int a = -2147483647-1;

to avoid the warning.

For the second declaration, you could write:

long long int b = 2147483647+1;

or

long long int b = 2147483648LL;

If you can't assume that int is at least 32 bits, things could
get a bit more complicated; there's no guarantee that the value
-2147483648 can be stored in an int.
 
J

Joe Pfeiffer

BartC said:
I have this code:

int a = -2147483648;
long long int b = 2147483648;

And gcc gives me this warning for each:

"warning: this decimal constant is unsigned only in ISO C90"

These types are signed 32 and 64 bits respectively. How to get rid of
this warning? What is it's problem anyway, in the second example?

(For the first, I guess that 2147483648 is out of range before the
negation is applied.)

The variables you're declaring are int and long long int, but the
constants are ints (note that the first one is a unary operator being
applied to a constant -- it isn't a negative constant. In the standard,
a decimal constant is just the digits, not the leading minus sign). The
compiler is warning you that ints can't represent those constants; they
have to be unsigned.

If you replace them with

int a = -2147483648u;
long long int b = 2147483648u;

(telling the compiler to make them unsigned)

or

int a = -2147483648ll;
long long int b = 2147483648ll;

(telling it you want them to be 64 bit)

the warnings go away, and the initialization successfully converts them
to what you had in mind.
 
K

Keith Thompson

Joe Pfeiffer said:
The variables you're declaring are int and long long int, but the
constants are ints (note that the first one is a unary operator being
applied to a constant -- it isn't a negative constant. In the standard,
a decimal constant is just the digits, not the leading minus sign). The
compiler is warning you that ints can't represent those constants; they
have to be unsigned.

Given INT_MAX==2147483647 and LONG_MAX==2147483647, 2147483648
cannot be of type int. It's of type unsigned long in C90, signed
long long in C99 and up. See my other followup for details.
If you replace them with

int a = -2147483648u;
long long int b = 2147483648u;

(telling the compiler to make them unsigned)

or

int a = -2147483648ll;
long long int b = 2147483648ll;

(telling it you want them to be 64 bit)

the warnings go away, and the initialization successfully converts them
to what you had in mind.

Adding a "u" suffix may inhibit the warning, but it won't
fix the problem that the unsigned-to-signed conversion has an
implementation-defined result. The "LL" suffix (don't use "ll", it
looks too much like "11") guarantees that the constant is signed, and
makes the conversion well defined (assuming int is at least 32 bits).
It kills portability to pre-C99 compilers; that may not be an issue.
 
B

BartC

BartC said:
int a = -2147483648;
long long int b = 2147483648;

And gcc gives me this warning for each:

"warning: this decimal constant is unsigned only in ISO C90"

Thanks for the replies. I've replaced instances of -2147483648 with
0x80000000 where an int is expected. And using an LL suffix for any long
long ints.

That's shut the compiler up for now. (These numbers occur in data
initialisation lists written by external software; the ints have to be
specially detected so as to generate the alternate form. Doubtless the same
problem will crop up elsewhere and will need fixing too. Odd that I can't
just write a legal int value, 0x80000000, in decimal!)

(I would have used the -cstd=C99 option in gcc if it had helped, but I tried
to use that yesterday to provide designated initialisers for one of my
complex structs, but it didn't like the 3-deep unnamed unions and structs
that it uses. Since I need that struct as it is, I think that C99 is out!)
 
K

Keith Thompson

BartC said:
Thanks for the replies. I've replaced instances of -2147483648 with
0x80000000 where an int is expected. And using an LL suffix for any long
long ints.

That eliminates the warning (because 0x8000000 is of type
unsigned int in both C90 and C99), but you're still doing an
implementation-defined conversion from unsigned to signed.
It's *probably* safe to assume that 0x80000000 converts to
(int)-2147483648; you'll have to decide whether that's good enough.

Using 2147483648LL should eliminate both the warning and the
implementation-definedness of the conversion -- if you can assume
support for the LL suffix.
That's shut the compiler up for now. (These numbers occur in data
initialisation lists written by external software; the ints have to be
specially detected so as to generate the alternate form. Doubtless the same
problem will crop up elsewhere and will need fixing too. Odd that I can't
just write a legal int value, 0x80000000, in decimal!)

Because 0x8000000 isn't a legal int value; INT_MAX on your system is
0x7FFFFFFF.
(I would have used the -cstd=C99 option in gcc if it had helped, but I tried
to use that yesterday to provide designated initialisers for one of my
complex structs, but it didn't like the 3-deep unnamed unions and structs
that it uses. Since I need that struct as it is, I think that C99 is out!)

C11 supports unnamed unions and structs. Depending on how
recent your gcc is, you can probably use "-std=c11 -pedantic" or
"-std=c1x -pedantic". (With "-std=c99" but without "-pedantic", gcc
doesn't complain about unnamed unions and structs.)
 
E

Eric Sosman

Thanks for the replies. I've replaced instances of -2147483648 with
0x80000000 where an int is expected. And using an LL suffix for any long
long ints.

That's shut the compiler up for now. (These numbers occur in data
initialisation lists written by external software; the ints have to be
specially detected so as to generate the alternate form. Doubtless the same
problem will crop up elsewhere and will need fixing too. Odd that I can't
just write a legal int value, 0x80000000, in decimal!)

Others have explained this, but the explanation doesn't seem
to have penetrated. Allow me to try again: From what you say, on
your system `int' has 32 significant bits, one of which is the
sign bit. Assuming no pad bits, INT_MAX is then 0x7fffffff. The
value 0x8000000 is

one greater than 0x7fffffff, hence

one greater than INT_MAX, hence

*not* "a legal int value."

Observing that 0x7fffffff is equal to 2147483647, we can also
reason that the value 2147483648 is

one greater than 2147483647, hence

one greater than INT_MAX, hence

*not* "a legal int value."

For your system's `int's, 0x80000000 and 2147483648 are out of
range, beyond the pale, in an alternate universe, in your dreams,
and pining for the fjords.
 
J

James Kuyper

Thanks for the replies. I've replaced instances of -2147483648 with
0x80000000 where an int is expected. And using an LL suffix for any long
long ints.

That's shut the compiler up for now. (These numbers occur in data
initialisation lists written by external software; the ints have to be
specially detected so as to generate the alternate form. Doubtless the same
problem will crop up elsewhere and will need fixing too. Odd that I can't
just write a legal int value, 0x80000000, in decimal!)

The fact that you can't write INT_MIN as an integer constant is due to
two things. First, integer constants cannot be negative: -9 is not a
negative integer constant, it's a unary minus expression whose operand
is an integer constant of 9. Personally, I don't think that was a good
idea. I'm not sure whether there's any code that would be broken if it
were changed; if not, I'd favor changing it.

Secondly, because 'int' has a 2's complement representation on your
system, INT_MAX + INT_MIN == -1. As a result, INT_MIN cannot be written
as -x for any x which is a constant of 'int' type.

Note that on your machine, 0x80000000 is not such a legal int value,
because it's value is greater than INT_MAX, and it's type is therefore
actually unsigned int. The fact that it converts to an int value of
-21474783648 is not guaranteed by the standard, not even on systems
where that is in fact the value of INT_MIN. Is there some reason you
can't use INT_MIN - for instance, you need your code to continue working
unchanged even if INT_MIN < -21474783648? In that case, I'd strongly
recommend using (-21474783647-1), which is the conventional work-around
for this long-standing problem.
 
B

BartC

James Kuyper said:
On 11/02/2012 05:03 PM, BartC wrote:
In that case, I'd strongly
recommend using (-21474783647-1), which is the conventional work-around
for this long-standing problem.

OK, will do. I used 0x80000000h because it was easier to type, and did an
equally good job of eliminating the warning. (And someone suggested using
it.)
 
B

Ben Pfaff

BartC said:
OK, will do. I used 0x80000000h because it was easier to type, and did
an equally good job of eliminating the warning. (And someone suggested
using it.)

Did you really include the "h" suffix?
 
B

BartC

Ben Pfaff said:
Did you really include the "h" suffix?

Don't think so; gcc would have complained. (The language used to generate
the C uses -h instead of 0x-. It must have slipped in above while I was
concentrating on getting the right number of zeros.)
 
L

Les Cargill

James said:
In C90, you'd be correct - which just means that it's out of range for
'int' both before, and after, the unary minus is evaluated.

In all fairness, every time I ever tested this, the implementation
did what was codified in the C90 spec. I have by long habit used
hexadecimal notation for anything that's remotely close
to this problem.
In C99 or later, it has type long long, which he's told us is a 64-bit
type on his system.


Oh, sorry I neglected that.
 
L

Les Cargill

Keith said:
That eliminates the warning (because 0x8000000 is of type
unsigned int in both C90 and C99), but you're still doing an
implementation-defined conversion from unsigned to signed.
It's *probably* safe to assume that 0x80000000 converts to
(int)-2147483648; you'll have to decide whether that's good enough.

Using 2147483648LL should eliminate both the warning and the
implementation-definedness of the conversion -- if you can assume
support for the LL suffix.

In my own defense :), I tried that and it failed.
C:\c\usenet>gcc -v
Reading specs from c:/Mingw/bin/../lib/gcc/mingw32/3.4.5/specs
Configured with: ../gcc-3.4.5-20060117-3/configure --with-gcc
--with-gnu-ld --with-gnu-as --host=mingw32 --target=mingw32
--prefix=/mingw --enable-threads --disable-nls
--enable-languages=c,c++,f77,ada,objc,java --disable-win32-registry
--disable-shared --
enable-sjlj-exceptions --enable-libgcj --disable-java-awt --without-x
--enable-java-gc=boehm --disable-libgcj-debug --enable-interpreter
--enable-hash-synchronization --enable-libstdcxx-debug
Thread model: win32
gcc version 3.4.5 (mingw-vista special r3)
Because 0x8000000 isn't a legal int value; INT_MAX on your system is
0x7FFFFFFF.

But let us be clear - 0x8000000 is not a legal value because it
maps to both numbers. There is no bijective map from 0x8000000
to the decimal representations - hence my "ambiguous".

The point I was trying to make is that hex notation is a strategy to
use when the other conventions break down. But as you note,
"you gotta know the territory."

<snip>
 
L

Les Cargill

BartC said:
OK, will do. I used 0x80000000h because it was easier to type, and did
an equally good job of eliminating the warning. (And someone suggested
using it.)

I suggested using it as something more like a heuristic, not
as a polished piece of Language Law. Indeed, the point I
was making was that there were two different decimal numbers
that map to 0x80000000.

For the case where long is 32 bits and long long is 64,
for the vast majority of implementations, it will work
*just fine*, but you really have to verify it and not trust it.
This is One Of Those Numbers.

Perhaps it has less value than I would prefer, but what I
put up is closer to what I'd have done in this case. What
the others have done is a detailed analysis, which is many
times more information dense than what I'd provided.

it's a matter of how deeply you need to drill.
 
K

Keith Thompson

Les Cargill said:
Keith said:
That eliminates the warning (because 0x8000000 is of type
unsigned int in both C90 and C99), but you're still doing an
implementation-defined conversion from unsigned to signed.
It's *probably* safe to assume that 0x80000000 converts to
(int)-2147483648; you'll have to decide whether that's good enough.

Using 2147483648LL should eliminate both the warning and the
implementation-definedness of the conversion -- if you can assume
support for the LL suffix.

In my own defense :), I tried that and it failed.
C:\c\usenet>gcc -v [snip]
gcc version 3.4.5 (mingw-vista special r3)

How did it fail?

For me, this program:

#include <stdio.h>
int main(void) {
int a = -2147483648LL;
long long int b = 2147483648LL;
printf("a = %d\n", a);
printf("b = %lld\n", b);
return 0;
}

compiles without warnings and produces this output:

a = -2147483648
b = 2147483648
But let us be clear - 0x8000000 is not a legal value because it
maps to both numbers. There is no bijective map from 0x8000000
to the decimal representations - hence my "ambiguous".

I'm not sure what you mean by "maps to both numbers". What numbers
are you referring to?

0x80000000 has a single mathematical value, namely 2,147,483,648.
Its type depends on the ranges of the fundamental types for
an implementation; it could be int, unsigned int, long int, or
unsigned long int. (Conveniently, it's the same in C90 and C99.)
It might map to different values if you convert it to some other
type, but that's not what makes it "not a legal value".
 
L

Les Cargill

Keith said:
Les Cargill said:
Keith said:
int a = -2147483648;
long long int b = 2147483648;

And gcc gives me this warning for each:

"warning: this decimal constant is unsigned only in ISO C90"

Thanks for the replies. I've replaced instances of -2147483648 with
0x80000000 where an int is expected. And using an LL suffix for any long
long ints.

That eliminates the warning (because 0x8000000 is of type
unsigned int in both C90 and C99), but you're still doing an
implementation-defined conversion from unsigned to signed.
It's *probably* safe to assume that 0x80000000 converts to
(int)-2147483648; you'll have to decide whether that's good enough.

Using 2147483648LL should eliminate both the warning and the
implementation-definedness of the conversion -- if you can assume
support for the LL suffix.

In my own defense :), I tried that and it failed.
C:\c\usenet>gcc -v [snip]
gcc version 3.4.5 (mingw-vista special r3)

How did it fail?

For me, this program:

#include <stdio.h>
int main(void) {
int a = -2147483648LL;
long long int b = 2147483648LL;
printf("a = %d\n", a);
printf("b = %lld\n", b);
return 0;
}

compiles without warnings and produces this output:

a = -2147483648
b = 2147483648

It warning-ed.

C:\c\usenet>gcc -o long.exe long.c
long.c:2: warning: this decimal constant is unsigned only in ISO C

C:\c\usenet>cat long.c

signed long int a = -2147483648L;
signed long long int b = 2147483648LL;

//signed long int a = 0x80000000L;
//signed long long int b = 0x80000000LL;

int main(void)
{
printf("a=%ld\n",a);
printf("b=%lld\n",b);

printf("a=%08lx\n",a);
printf("b=%08llx\n",b);
return 0;
}

C:\c\usenet>
C:\c\usenet>
C:\c\usenet>long.exe
a=-2147483648
b=2147483648
a=80000000
b=80000000

C:\c\usenet>

I'm not sure what you mean by "maps to both numbers". What numbers
are you referring to?

There are two strings embedded in the source code - -2147483648
and 2147483648 - which map to the same hex value, for the right toolsets
and declarations. As you note, -2147483648 is actually an expression,
but it is still, SFAIK, a constant value.
 

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
473,766
Messages
2,569,569
Members
45,043
Latest member
CannalabsCBDReview

Latest Threads

Top