| > #include <iostream.h>
> int main(int argc, const char* argv[])
> {
> unsigned char uc = 0xeb;
> unsigned char ucOut1 = uc<<4;
> unsigned char ucOut2 = (uc<<4)>>4;
> cout<<hex<<unsigned(ucOut1)<<' '<<unsigned(ucOut2)<<endl;
> // prints "b0 eb" while it should print "b0 0b"
> return 0;
> }
The short answer is that the "b0 eb" output is correct,
because in the expression
(uc<<4)>>4
the unsigned char uc is promoted to int and the expression is
evaluated as "(int << int) >> int" with an int result.
The long answer points to both:
usual arithmetic conversions
integral promotions
First, from the DWP on usual arithmetic conversions:
9 Many binary operators that expect operands of arithmetic or enumera-
tion type cause conversions and yield result types in a similar way.
The purpose is to yield a common type, which is also the type of the
result. This pattern is called the usual arithmetic conversions,
which are defined as follows:
--If either operand is of type long double, the other shall be con-
verted to long double.
--Otherwise, if either operand is double, the other shall be converted
to double.
--Otherwise, if either operand is float, the other shall be converted
to float.
--Otherwise, the integral promotions (_conv.prom_) shall be performed
on both operands.1)
--Then, if either operand is unsigned long the other shall be con-
verted to unsigned long.
--Otherwise, if one operand is a long int and the other unsigned int,
then if a long int can represent all the values of an unsigned int,
the unsigned int shall be converted to a long int; otherwise both
operands shall be converted to unsigned long int.
--Otherwise, if either operand is long, the other shall be converted
to long.
--Otherwise, if either operand is unsigned, the other shall be con-
verted to unsigned.
[Note: otherwise, the only remaining case is that both operands are
int ]
_________________________
1) As a consequence, operands of type bool, wchar_t, or an enumerated
type are converted to some integral type.
Next, from the DWP on integral promotions:
4.5 Integral promotions [conv.prom]
1 An rvalue of type char, signed char, unsigned char, short int, or
unsigned short int can be converted to an rvalue of type int if int
can represent all the values of the source type; otherwise, the source
rvalue can be converted to an rvalue of type unsigned int.
2 An rvalue of type wchar_t (_basic.fundamental_) or an enumeration type
(_dcl.enum_) can be converted to an rvalue of the first of the follow-
ing types that can represent all the values of its underlying type:
int, unsigned int, long, or unsigned long.
3 An rvalue for an integral bit-field (_class.bit_) can be converted to
an rvalue of type int if int can represent all the values of the bit-
field; otherwise, it can be converted to unsigned int if unsigned int
can represent all the values of the bit-field. If the bit-field is
larger yet, no integral promotion applies to it. If the bit-field has
an enumerated type, it is treated as any other value of that type for
promotion purposes.
4 An rvalue of type bool can be converted to an rvalue of type int, with
false becoming zero and true becoming one.
5 These conversions are called integral promotions.
> Without converting an unsigned char variable to unsigned() in the
> streaming, the output would treat it as a char (as opposed to an
> integral value) and print a graphic of 'e' with two dots on its
> top. Is this another bug, or does C++ standard leave it to
> implementation, or is this exactly as specified by the standard?
The arithmetic inserters are...
27.6.2.5.2 Arithmetic [lib.ostream.inserters.arithmetic]
Inserters
operator<<(bool val);
operator<<(short val);
operator<<(unsigned short val);
operator<<(int val);
operator<<(unsigned int val);
operator<<(long val);
operator<<(unsigned long val);
operator<<(float val);
operator<<(double val);
operator<<(long double val);
operator<<(void* val);
The inserters for char, signed char, and unsigned char are not
arithmetic inserters. Instead they all insert the specified
character to the output stream as a character, not as a
byte-sized integer. If they want to format the integral value
of uc or ucOut1 or ucOut2 then they do need the cast as they
already used in their example.
Dan
|