casting unsigned integers

T

techie

I have defined a number of unsigned integer types as follows:

typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned int uint32;
typedfe long long uint64;

Is it necessary to explicitly cast from one type of unsigned integer type to
another even though they do so implicitly?

e.g. bytes0_1 |= (static_cast<uint16>(VersionNo)) << 12;

bytes0_1 is of type uint16. Here I thought it is safer to cast VersionNo
(type uint8) to uint16 before I do a left shift. I was just a bit worried
about shifting the digits off the end of an 8 bit number. Likewise in the
statement below I cast the result of the left shift and bit wise addition to
uint64.

byte6 = static_cast<uint8>((MAC_Adddress >> 40) & 0xFF);

MAC_Adddress is of type uint64.

When I run QA C++ (source code analyzer) on my code it issues a few warnings
for the first statement:

Bitwise operator is being applied to a signed type.
This is an implicit conversion between signed and unsigned integer types.
Be aware that an implicit conversion from 'uint16' to 'int' takes place.

I think I can ignore the first two warnings as the types are actually
unsigned integers and not signed as it thinks they are. I think the third
one means that in order to do the left shift it does an implicit conversion
to int as operator << is just defined for int.

Are these static_casts necessary? Is there a better way to write these
statements?
 
S

Steve Pope

techie said:
I have defined a number of unsigned integer types as follows:

typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned int uint32;
typedfe long long uint64;
Is it necessary to explicitly cast from one type of unsigned integer type to
another even though they do so implicitly?

e.g. bytes0_1 |= (static_cast<uint16>(VersionNo)) << 12;

bytes0_1 is of type uint16. Here I thought it is safer to cast VersionNo
(type uint8) to uint16 before I do a left shift. I was just a bit worried
about shifting the digits off the end of an 8 bit number. Likewise in the
statement below I cast the result of the left shift and bit wise addition to
uint64.

byte6 = static_cast<uint8>((MAC_Adddress >> 40) & 0xFF);

MAC_Adddress is of type uint64.

When I run QA C++ (source code analyzer) on my code it issues a few warnings
for the first statement:

Bitwise operator is being applied to a signed type.

In your first expression the right-hand side has two operands.
The wider of these operands is the constant 12, which is
of type int, and is therefore (probably) 32 bits wide (on
many systems). The other operand, which is the expression
(static_cast<uint16>(VersionNo)), is promoted to being 32 bits
wide to match the wider operand. Then the 12 is promoted to
unsigned (if either operand is unsigned, the other becomes
unsigned). The result of the shift operation is an unsigned
int (32 bits), which is then automatically cast down to the width
of the left hand side, which is 16 bits unsigned.

I do not particularly understand why the compiler gave you this
particular warning, but it seems to derive from the implicit
promotion of the signed constant 12 to an unsigned value.

Steve
 
J

Jack Klein

In your first expression the right-hand side has two operands.
The wider of these operands is the constant 12, which is
of type int, and is therefore (probably) 32 bits wide (on
many systems). The other operand, which is the expression
(static_cast<uint16>(VersionNo)), is promoted to being 32 bits
wide to match the wider operand.

No, that is not correct for the bitwise shift operators. The left and
right operands do not need to be converted to the same type.

The integer promotions would apply to the argument on the right if and
only if it were a narrower type than int. Since the plain integer
literal "12" already has type int, no change is necessary. This has
nothing at all to do with the number of bits in an int.

The cast forces a conversion "VersionNo" from unsigned char to
unsigned short. Then it is automatically converted to either int or
unsigned int. Since the OP has told us by his typedef that int has 32
bits on his platform, and a signed 32-bit int can hold all the
possible values of an unsigned 16-bit short, it is converted to signed
int, not unsigned int.

See paragraph 1 of 4.5 of the C++ standard.
Then the 12 is promoted to
unsigned (if either operand is unsigned, the other becomes
unsigned).

The type of the left hand operand in bitwise shift operator
expressions has nothing at all to do with the type or any conversions
performed on the right hand operator. "12" starts out as an int and
stays an int.

There is no need to convert the two operands of the bitwise shift
operators to the same type, as they are never directly combined. The
bitwise shift binary operators are different from the arithmetic and
bitwise AND, OR, and XCR operations in this respect.

See 5.8 of the C++ standard, and note the omission of the phrase "the
usual arithmetic conversions are performed".
The result of the shift operation is an unsigned
int (32 bits), which is then automatically cast down to the width
of the left hand side, which is 16 bits unsigned.

There are two errors in the paragraph above. First is the fact that
since the left hand operand of the shift operator is a signed int, the
result also has the type signed int.

It is not "automatically cast", there is no such thing. C++ has
conversions, some of which are automatic and some of which require a
cast. A cast is only performed by one of the cast operators, and
tells the compiler to perform an explicit conversion, one which might
or might not have happened automatically in the absence of the cast.

The OP did not specify the type of the destination "bytes0_1", but if
it is not signed int, the result of the shift operator is
automatically converted to that type.
I do not particularly understand why the compiler gave you this
particular warning, but it seems to derive from the implicit
promotion of the signed constant 12 to an unsigned value.

There is no promotion or conversion at all of the integer literal "12"
to an unsigned value. It has type int, and it remains type int in the
expression.

The warning is because the unsigned 16-bit short promoted to a signed
32-bit int.

C++ inherits its integer promotion rules from ISO C, and those rules
are "value preserving" rather than "sign preserving".

When an unsigned integer type of lesser rank than int is being
promoted to int, it is promoted to signed int if the entire range of
values of the lesser unsigned type is within the range of positive
values in a signed int.

On an implementation with 16-bit shorts and 16-bit ints, an unsigned
short would promote to unsigned int. On an implementation with 16-bit
shorts and 32-bit ints, an unsigned short promotes to signed int.
 
T

techie

I have now modified my code to the following:

uint32 sum = 0;
sum |= static_cast<utils::uint32>(MajorVer) << 12;
sum |= static_cast<utils::uint32>(MinorVer) << 4;
sum |= static_cast<utils::uint32>(Config);
uint16 bytes0_1 = static_cast<uint16>(sum);

Here MajorVer, MinorVer and Config are of type uint8. I am combining their
values into a 16 bit integer. As I am using a uint32 variable (sum) to do
the addition, which is the size of int, I get no warnings at all.

I could leave out the static_cast<utils::uint32> operations and just allow
for implicit conversion to int but PRQA gives me a maintenance warning about
that. Explicitly casting to uint32 shows the intent.
 
S

Steve Pope

Jack Klein said:
(Steve Pope) wrote in comp.lang.c++:
No, that is not correct for the bitwise shift operators. The left and
right operands do not need to be converted to the same type.
The integer promotions would apply to the argument on the right if and
only if it were a narrower type than int.

Please re-read what I wrote above. I said that the operand on
the *left* is promoted to 32 bits, to match the operand on the right.
I did not in the above say the operand on the right was promoted.
The cast forces a conversion "VersionNo" from unsigned char to
unsigned short. Then it is automatically converted to either int or
unsigned int. Since the OP has told us by his typedef that int has 32
bits on his platform, and a signed 32-bit int can hold all the
possible values of an unsigned 16-bit short, it is converted to signed
int, not unsigned int.
See paragraph 1 of 4.5 of the C++ standard.

See 4.7 -- "The conversions allowed as integral promotions [e.g.
in 4.5] are excluded from the set of integral conversions".
This means, I believe, that if there is a need to convert to unsigned
based on the operands of the expression, then that need has precedence
over the possibility of storing an unsigned value in a signed quantity
if there are enough bits.

(In practice, it doesn't matter since the result is the same.)
The type of the left hand operand in bitwise shift operator
expressions has nothing at all to do with the type or any conversions
performed on the right hand operator. "12" starts out as an int and
stays an int.
There is no need to convert the two operands of the bitwise shift
operators to the same type, as they are never directly combined. The
bitwise shift binary operators are different from the arithmetic and
bitwise AND, OR, and XCR operations in this respect.
See 5.8 of the C++ standard, and note the omission of the phrase "the
usual arithmetic conversions are performed".

I'll agree this could be interpreted to mean that shift operators
are treated differently from other arithmetic operators in terms
of forcing conversions to unsigned, but it doesn't really say
this explicitly.
There are two errors in the paragraph above. First is the fact that
since the left hand operand of the shift operator is a signed int,

That is true is your first argument above is correct, and I'm
not yet convinced.
It is not "automatically cast", there is no such thing. C++ has
conversions, some of which are automatic and some of which require a
cast. A cast is only performed by one of the cast operators, and
tells the compiler to perform an explicit conversion, one which might
or might not have happened automatically in the absence of the cast.

Okay, my terminology was incorrect there.
The OP did not specify the type of the destination "bytes0_1"

Yes, he did, see quoted text above.
if it is not signed int, the result of the shift operator is
automatically converted to that type.

Yes, and it is converted. (But not "automatically cast" as you
point out.)
C++ inherits its integer promotion rules from ISO C, and those rules
are "value preserving" rather than "sign preserving".

Is this something that changed between K&R C and ISO C? The
rules in K&R section 2.7, "Type conversions" are as I describe,
and it is my belief that you will never get a different result
in C++ than these rules would imply, even if the C++ rules are
a bit different in some intermediate details.

Thanks for you post.

Steve
 

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,767
Messages
2,569,570
Members
45,045
Latest member
DRCM

Latest Threads

Top