[Search for users] [Overall Top Noters] [List of all Conferences] [Download this site]

Conference turris::c_plus_plus

Title:C++
Notice:Read 1.* and use keywords (e.g. SHOW KEY/FULL KIT_CXX_VAX_VMS)
Moderator:DECCXX::AMARTIN
Created:Fri Nov 06 1987
Last Modified:Thu Jun 05 1997
Last Successful Update:Fri Jun 06 1997
Number of topics:3604
Total number of notes:18242

3512.0. "left shift, right shift" by CSC32::I_WALDO () Mon Mar 24 1997 20:26

    The following is from a customer.  I played with this a bit on both VAX
    VMS (6.2) as well as UNIX 4.0 and got the same results.  Appearantly a
    left shift of a char doesn't dispose of the shifted out bits, ie a char
    isn't really a char but something longer. 
    ************************************************************************ 
    
I suspect that DEC compiler version 5.5 on UNIX version 3.2D
has a bug that can be revealed by the following test code:


#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 impact is that we can not have a faster implementation of getNibble:

   typedef unsigned char BYTE;
   typedef unsigned char UINT8;
   BYTE getNibble(const BYTE& byte, UINT8 nibbleIndexHi0Lo1)
   {return (byte<< (nibbleIndexHi0Lo1<<2) ) >> 4;}

as opposed to the supposedly slower

   BYTE getNibble(const BYTE& byte, UINT8 nibbleIndexHi0Lo1)
   { return nibbleIndexHi0Lo1 ?(byte&LOW_MASK) : (byte&HIGH_MASK)>>4; }


Is this a bug or my understanding of ANSI C standard on this is wrong?

What is a workaround if it is a bug?

What would be an even faster version from compiler implementation
point of view?

Issue 2:
    
    
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?

T.RTitleUserPersonal
Name
DateLines
3512.1SPECXN::DERAMODan D'EramoMon Mar 24 1997 21:27130
>	#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
3512.2much appreciated!CSC32::I_WALDOTue Mar 25 1997 13:243
    Thank you very much for the detailed response.
    
    Irving