casting unsigned integers

Discussion in 'C++' started by techie, Oct 12, 2006.

  1. techie

    techie Guest

    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?
    techie, Oct 12, 2006
    #1
    1. Advertising

  2. techie

    Steve Pope Guest

    techie <> wrote:

    >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
    Steve Pope, Oct 12, 2006
    #2
    1. Advertising

  3. techie

    Jack Klein Guest

    On Thu, 12 Oct 2006 05:30:24 +0000 (UTC),
    (Steve Pope) wrote in comp.lang.c++:

    > techie <> wrote:
    >
    > >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.


    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.

    --
    Jack Klein
    Home: http://JK-Technology.Com
    FAQs for
    comp.lang.c http://c-faq.com/
    comp.lang.c++ http://www.parashift.com/c -faq-lite/
    alt.comp.lang.learn.c-c++
    http://www.contrib.andrew.cmu.edu/~ajo/docs/FAQ-acllc.html
    Jack Klein, Oct 13, 2006
    #3
  4. techie

    techie Guest

    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.
    techie, Oct 13, 2006
    #4
  5. techie

    Steve Pope Guest

    Jack Klein <> wrote:

    > (Steve Pope) wrote in comp.lang.c++:


    >>> typedef unsigned short uint16;
    >>> bytes0_1 |= (static_cast<uint16>(VersionNo)) << 12;
    >>> bytes0_1 is of type uint16.


    >> 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.


    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.)

    >> 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".


    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.

    >> 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,


    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
    Steve Pope, Oct 13, 2006
    #5
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. jeff
    Replies:
    2
    Views:
    2,627
    Mark Thornton
    Mar 7, 2004
  2. Edith Gross
    Replies:
    5
    Views:
    1,108
    Jerry Coffin
    May 1, 2005
  3. Jonathan Fielder

    Casting integers to float.

    Jonathan Fielder, Aug 12, 2003, in forum: C Programming
    Replies:
    8
    Views:
    483
    Christian Bau
    Aug 13, 2003
  4. Alex Vinokur
    Replies:
    9
    Views:
    773
    James Kanze
    Oct 13, 2008
  5. pozz
    Replies:
    12
    Views:
    723
    Tim Rentsch
    Mar 20, 2011
Loading...

Share This Page