A C vs C++ trivia question about integer constants

Here a question for C/C++ programmers with more experience than mine: I did not know until just now that the expression

p = (0 << 24) + (g << 16) + (b << 8 ) + r;

will give you bogus results even if r, g, b, and p are unsigned long. The correct expression is

p = (0L << 24L) + (g << 16L) + (b << 8L ) + r;

which avoids overflowing (g << 16L), which is otherwise evaluated as an integer and always comes up 0. Indeed, the Arduino IntegerConstants web page clearly states “By default, an integer constant is treated as an int with the attendant limitations in values” so this seems to be the expected behavior.

I was under the impression with ‘g’ being a long, that the expression ‘g << 16’ would be evaluated as a long. This doesn’t seem to be the case. Is this a C++ specific thing? Or to C and C++? Or is it “implementation-specific”?

Thanks in advance,
w

Post a sketch which demonstrates the bogus results.

This sketch prints 65536 as expected:

void setup(void)
{
  Serial.begin(9600);
  while(!Serial);

  long result;
  long r = 1;
  result = (r << 16);
  Serial.println(result);
}

void loop(void)
{
}

Pete

Oh great, I made a stripped down example to post (because the sketch is rather long) and now the expression works fine without forcing the constants to long. I must be flubbing something, somewhere. Standby ....

Dangit, now the entire sketch works without the long contants.

I guess this will forever remain a mystery until I start using version control :blush:

Anyway, here’s stripped down sketch someone might find useful. It sends a list of 50 RGB values to one of those TM1803 based LED strips like this one: http://www.radioshack.com/product/index.jsp?productId=16306856 Radio Shack had them on sale for $8 so I bought five, they are now overpriced compared to what you can get at Sparkfun, Adafruit, et al.

// Chroma50.ino: sends a list of 50 RGB values with equidistant hue values
// and constant saturation and brightness to strip of 5 of 
// TM1803 based LED strips like this one:
// http://www.radioshack.com/product/index.jsp?productId=16306856 Radio
// Shack had them on sale for $8 so I bought five, they are now
// overpriced compared to what you can get at Sparkfun, Adafruit, et
// al. Other LED strips behave similarly


// define the pin(s) to send LED programming signals
#define DATA_1 (PORTF |=  0X0F)    // DATA 1    // for MEGA - ports A0,A1,A2
#define DATA_0 (PORTF &=  0XF0)    // DATA 0    // for MEGA - (all F ports)
#define STRIP_PINOUT DDRF=0xFF  // for ATMEGA sets all port F pins as OUTPUT
// #define DATA_1 (PORTC |=  0X01)    // DATA 1    // for UNO port A0
// #define DATA_0 (PORTC &=  0XFE)    // DATA 0    // for UNO port A0
// #define STRIP_PINOUT (DDRC=0xFF)    // for UNO
 
// For the Radio Shack 276-249 strip, The longs you send are in a weird format, GBR
// for example
/*
 #define RED 0x0000ff
 #define RE0 0x00000f
 #define ORG 0x7000ee
 #define YEL 0xfc00fd
 #define GRN 0xff0000
 #define CYN 0x666600
 #define BLU 0x00ff00
 #define VIO 0x004040
 #define MAG 0x00ffff
 #define WHT 0xffffff
 #define BLK 0x000000
 */

void setup() {  
    Serial.begin(19200);
    pinMode(13,OUTPUT);
    digitalWrite(13, LOW);              
    // Mega: A pot on pins A8,A10,12 will control saturdation
    pinMode(A12, OUTPUT);
    digitalWrite(A12, LOW);
    pinMode(A8, OUTPUT);
    digitalWrite(A8, HIGH);
    pinMode(A10, INPUT);

    STRIP_PINOUT;        // set output pin
    reset_strip();
}

unsigned long p[] = {
    0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0};
int j = 0;
void loop() 
{
    float h,s,v;
    unsigned long r,g,b;
    int i, whichpat;
    int d=200;
    unsigned long *pat[1][50], brt;
    s  = 1.0 - (analogRead(A10) / 1023.0);
    v = 0.1; // 255
    h = j * 360 / 50;
    for (i=49; i>0; i--) {
            p[i] = p[i-1];
            pat[0][i] = &p[i];
    }
    HSVtoRGB(&r, &g, &b, h, s, v);
    p[0] = (0<<24) + (g<<16) + (b<<8) + r;
    pat[0][0] = &p[0];
    j = (j+1) % 50;
    printhsv(&r, &g, &b, h, s, v, *pat[0][0]);
    send_pattern(pat,5,300);
}

void send_pattern(unsigned long* data[][50], int meters, int frame_rate)
/* non progmem version similar to what is in example codes*/
{
    int i=0;
    int j=0;
    uint32_t temp_data;
    uint32_t bright;

    noInterrupts();
    for (j=0;j<meters*10;j++)
    {
        temp_data = *data[i][j];
        send_strip(temp_data);
    }
    interrupts();
    delay(frame_rate);
}

/*******************************************************************************
 * Function Name  : send_strip (came with examples)
 * Description    : Transmit 24 pulse to LED strip. Timing frobbery with __asm__
 *                  
 * Input          : 24-bit data for the strip
 * 
 *******************************************************************************/
void send_strip(uint32_t data)
{
    int i;
    unsigned long j=0x800000;


    for (i=0;i<24;i++)
    {
        if (data & j)
        {
            DATA_1;
            __asm__("nop\n\t");
            __asm__("nop\n\t");
            __asm__("nop\n\t");
            __asm__("nop\n\t");
            __asm__("nop\n\t");
            __asm__("nop\n\t");
            __asm__("nop\n\t");
            __asm__("nop\n\t");
            __asm__("nop\n\t");    
            __asm__("nop\n\t");
            __asm__("nop\n\t");
            __asm__("nop\n\t");
            __asm__("nop\n\t");
            __asm__("nop\n\t");
            __asm__("nop\n\t");
            __asm__("nop\n\t");
            __asm__("nop\n\t");
            __asm__("nop\n\t");

            /*----------------------------*/
            __asm__("nop\n\t");
            __asm__("nop\n\t");
            __asm__("nop\n\t");
            __asm__("nop\n\t");
            __asm__("nop\n\t");  
            __asm__("nop\n\t");
            __asm__("nop\n\t");
            __asm__("nop\n\t");  
            __asm__("nop\n\t");  
            __asm__("nop\n\t");        
            /*----------------------------*/
            DATA_0;
        }
        else
        {
            DATA_1;
            __asm__("nop\n\t");
            __asm__("nop\n\t");
            __asm__("nop\n\t");
            __asm__("nop\n\t");
            __asm__("nop\n\t");
            __asm__("nop\n\t");
            __asm__("nop\n\t");
            __asm__("nop\n\t");
            __asm__("nop\n\t");    
            DATA_0;
            /*----------------------------*/
            __asm__("nop\n\t");
            __asm__("nop\n\t");
            __asm__("nop\n\t");      
            /*----------------------------*/
        }

        j>>=1;
    }



}

void    reset_strip()
{
    DATA_0;
    delayMicroseconds(20);
}



void HSVtoRGB(unsigned long *r, unsigned long *g, unsigned long *b, float h, float s, float v)
{
   int i;
   float f, p, q, t;

   if( s == 0 ) {
       // achromatic (grey)
       *r = *g = *b = v * 255;
       return;
   }

   h /= 60;            // sector 0 to 5
   i = floor( h );
   f = h - i;          // factorial part of h
   p = v * ( 1 - s );
   q = v * ( 1 - s * f );
   t = v * ( 1 - s * ( 1 - f ) );

   switch( i ) {
       case 0:
           *r = v * 255;
           *g = t * 255;
           *b = p * 255;
           break;
       case 1:
           *r = q * 255;
           *g = v * 255;
           *b = p * 255;
           break;
       case 2:
           *r = p * 255;
           *g = v * 255;
           *b = t * 255;
           break;
       case 3:
           *r = p * 255;
           *g = q * 255;
           *b = v * 255;
           break;
       case 4:
           *r = t * 255;
           *g = p * 255;
           *b = v * 255;
           break;
       default:        // case 5:
           *r = v * 255;
           *g = p * 255;
           *b = q * 255;
           break;
   }

}

void printhsv(unsigned long *r,unsigned  long *g,unsigned  long *b, float h, float s, float v ,unsigned  long p) {
    Serial.print("R: ");
    Serial.print(*r);
    Serial.print("   G: ");
    Serial.print(*g);
    Serial.print("   B: ");
    Serial.print(*b);
    Serial.print("   H: ");
    Serial.print(h);
    Serial.print("   S: ");
    Serial.print(s);
    Serial.print("   V: ");
    Serial.print(v);
    Serial.print("   P: ");
    Serial.println(p, HEX);
}

Most binary operators practice contagion from either argument, you are probably thinking of

  long foo = 1 << 16 ;

Where both arguments are int by default,

In the case of the shift operators, I don’t think it matters whether the number of bits being shifted is specified as long or 16-bit int - what matters is the type of the number being shifted.

// This results in zero
long foo = 1 << 16;
// This also results in zero
foo = 1 << 16L;
// but this gives 65536
foo = 1L << 16;

Pete

el_supremo:
In the case of the shift operators, I don’t think it matters whether the number of bits being shifted is specified as long or 16-bit int - what matters is the type of the number being shifted.

Pete

Yup, you’re onto it.

<< & >> are evaluated left to right, so integral promotion rules won’t promote a smaller left hand side to a larger right hand value. Primarily because the left hand side denotes the size of the result.

Here is something from the standard:

5.8 Shift operators [expr.shift]
1 The shift operators << and >> group left-to-right.
shift-expression:
additive-expression
shift-expression << additive-expression
shift-expression >> additive-expression
The operands shall be of integral or unscoped enumeration type and integral promotions are performed.
The type of the result is that of the promoted left operand. The behavior is undefined if the right operand
is negative, or greater than or equal to the length in bits of the promoted left operand.
2 The value of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are zero-filled. If E1 has an unsigned
type, the value of the result is E1 × 2
E2, reduced modulo one more than the maximum value representable
in the result type. Otherwise, if E1 has a signed type and non-negative value, and E1 × 2
E2 is representable
in the result type, then that is the resulting value; otherwise, the behavior is undefined.
3 The value of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has an unsigned type or if E1 has a signed
type and a non-negative value, the value of the result is the integral part of the quotient

The promoted left operand refers to a left hand value that would be promoted if it was smaller than int, like a char.