_BV() macro problem and question

Hi all,

I’ve discovered that the “[b]_BV()[/b]” macro (located in “[b]arduino-1.0.x/hardware/tools/avr/lib/avr/include/avr/sfr_defs.h[/b]”) doesn’t work if the bit value is 15 or higher.

The macro is simple:

	#define _BV(bit) (1 << (bit))

My theory as to why it fails is that the value shifted (1) is by default an int (i.e. a signed 16 bit number which cannot be larger than +32767). To test my theory, I changed the macro to this…

	#define _BV(bit) ((uint64_t) 1 << (bit))

…and ta-daa it works.

My questions are:

  • Will this still operate as before (i.e. expanded by the pre-processor and only be a value, not code)?
  • Should I use uint64_t, uint32_t or size_t?
  • Should I replace the macro with a bunch of pre-defines (like this):
	#define _BV(0) 0x0000000000000001
	#define _BV(1) 0x0000000000000002
	#define _BV(2) 0x0000000000000004
	// . . .
	#define _BV(61) 0x2000000000000000
	#define _BV(62) 0x4000000000000000
	#define _BV(63) 0x8000000000000000
  • Do something else?
  • Leave it alone, as is with the flaw?

Any input will be appreciated. Thanks!

– Roger

Should I use uint64_t, uint32_t or size_t?

Not uint64_t. A 64 bit mask is not very useful with AVR processors and 64 bit values are strange nasty beasts in that world.

Should I replace the macro with a bunch of pre-defines (like this):

I’m guessing you have not yet tried to compile those lines.

Leave it alone, as is with the flaw?

Were I in your shoes, I would correct the problem for 15…

#define _BV(bit) (1U << (bit))

In general, a template would probably work very well. Or, just use the bit macro.

(1) I usually don’t need anything 64 bit, but I thought why not give it all the functionality possible?

(2) You’re right. I didn’t at all like that idea, I simply tossed it in there as a possibility.

(3) Wow, I didn’t know about the bit macro. It’s the same as _BV (except IT works). I wonder why there are two different macros that do the same thing (aside from the “int bugginess” of _BV)?

(4) Looking at the bit macro, I see they do the same thing I was trying to do (cast the TYPE of the value “1”).

So, why not use “1ULL” to cover every possibility? Any reason NOT to?

And, what do you mean by using a “template”? I have no idea what that means.

BTW, thanks for the help and info!

– Roger

Use bit() instead.

In arduino.h:

#define bit(b) (1UL << (b))

Krupski:
(1) I usually don’t need anything 64 bit, but I thought why not give it all the functionality possible?

Note: 64 bit expressions are very expensive with avr-gcc.

The problem is that it forces the compiler’s hand. The rest of the expression has to be evaluated in 64 bit. Because the value is a constant, the compiler will typically optimize to a more appropriate size but that may not always be the case.

(3) Wow, I didn’t know about the bit macro. It’s the same as _BV (except IT works). I wonder why there are two different macros that do the same thing (aside from the “int bugginess” of _BV)?

_BV is included with AVR Libc. It is primarily intended to be used for 8 bit bitmasks.

I don’t know why the Arduino folks included bit. I suspect it is left over from Processing.

So, why not use “1ULL” to cover every possibility? Any reason NOT to?

Try printing the value…

Serial.print( (1ULL < 0) );

And, what do you mean by using a “template”? I have no idea what that means.

Don’t worry about it. I can’t find much if any value in using a template for this problem. bit is the better choice.

BTW, thanks for the help and info!

You are welcome.

find below a simple test I just wrote to compare timing of different _BV() implementations.
(UNO IDE 1.5.4)

Result is that this define is the fastest 0…63 shift supporting macro I think of in the time to write the sketch.

#define _BV(bit) ((bit < 32) ? (1UL << bit) : ( 1ULL << bit))

//
//    FILE: BVtest.ino
//  AUTHOR: Rob Tillaart
// VERSION: 0.1.02
// PURPOSE: 
//    DATE: 2014-dec-07
//     URL:
//
// Released to the public domain
//

#define _BV0(bit) ((bit < 32) ? ((bit < 16)? (1 << bit):(1UL << bit)):( 1ULL << bit) )
#define _BV1(bit) ((bit < 32) ? (1UL << bit):( 1ULL << bit) )
#define _BV2(bit) ((bit < 32) ? (1UL << bit):( (0x80000000ULL) << (bit-31)))
#define _BV3(bit) (((bit < 32) ? 1UL:1ULL) << bit)
#define _BV4(bit) (1ULL << bit)

uint32_t start;
uint32_t stop;

volatile uint64_t z;

void setup() 
{
  Serial.begin(115200);
  Serial.println("Start ");

  for (int i=0; i<64; i++)
  {
    uint64_t z1 = _BV1(i);
    uint64_t z2 = _BV2(i);
    Serial.print(i);
    Serial.print('\t');
    Serial.print((uint32_t)(z1 >> 32), HEX);  
    Serial.print('\t');
    Serial.println((uint32_t)(z2 >> 32), HEX);  
  }
  delay(1000);

  /******************************************/

  start = micros();
  for (uint8_t i=0; i<64; i++)
  {
    z = _BV0(i);
  }
  stop = micros();
  Serial.println(stop-start);
  delay(1000);

  start = micros();
  for (uint8_t i=0; i<64; i++)
  {
    z = _BV1(i);
  }
  stop = micros();
  Serial.println(stop-start);
  delay(1000);

  start = micros();
  for (uint8_t i=0; i<64; i++)
  {
    z = _BV2(i);
  }
  stop = micros();
  Serial.println(stop-start);
  delay(1000);

  start = micros();
  for (uint8_t i=0; i<64; i++)
  {
    z = _BV3(i);
  }
  stop = micros();
  Serial.println(stop-start);
  delay(1000);

  start = micros();
  for (uint8_t i=0; i<64; i++)
  {
    z = _BV4(i);
  }
  stop = micros();
  Serial.println(stop-start);
  delay(1000);

  /******************************************/

  start = micros();
  for (uint8_t i=0; i<32; i++)
  {
    z = _BV(i);
  }
  stop = micros();
  Serial.print("_BV():\t");
  Serial.println(stop-start);
  delay(1000);

  start = micros();
  for (uint8_t i=0; i<32; i++)
  {
    z = _BV1(i);
  }
  stop = micros();
  Serial.print("0..31:\t");
  Serial.println(stop-start);
  delay(1000);

  start = micros();
  for (uint8_t i=32; i<64; i++)
  {
    z = _BV1(i);
  }
  stop = micros();
  Serial.print("32..64:\t");
  Serial.println(stop-start);
  delay(1000);

  start = micros();
  for (uint8_t i=0; i<64; i++)
  {
    z = _BV4(i);
  }
  stop = micros();
  Serial.print("1ULL:\t");
  Serial.println(stop-start);
  delay(1000);

  Serial.println("\ndone...");

}

void loop() 
{
}

_BV(): 224 the reference supporting only 0…31

0…31: 288 the proposed is 25% slower for 0…31
32…64: 632 the proposed is ~3x slower for 32…63

1ULL: 1720 much slower …

The simple 1ULL << bit version is probably harder to optimize for the compiler.
(did not want to dive into the assembly code)

Nice use of the ternary operator!

Small correction (add one U)…

#define _BV0(bit) ((bit < 32) ? ((bit < 16)? (1U << bit):(1UL << bit)):( 1ULL << bit) )

Add a case for less than zero?

Huh. bit probably should in parenthesis (in the original versions as well)…

#define _BV0(bit) (((bit) < 32) ? (((bit) < 16)? (1U << (bit)):(1UL << (bit))):( 1ULL << (bit)) )

Add a case for less than zero?

Serial.println( 16 << -1 ); ==> 8 // so x << -y works like x >> y [UNO, 1.5.4]

so yes the macros is not complete… so the macro would become something like

#define _BV5(bit) (((bit) < -31) || ((bit) > 31)) ? (1ULL << (bit)) : ( 1UL << (bit)) )

the additional test would make it slower.
(quick test)
BV5: 1720 // same as 1ULL<< (bit)

So conclusion is that optimizing the BV macro for 64 bit only is possible if the range of bits is reduced to positive bits only .

thanks CB for this lesson !

Why less than zero? There will never be a negative number of bits.

The value of _BV (3) is, for example, 8.

The value of _BV (0) is 1.

There’s no such thing as _BV (-1)! :slight_smile:

Why less than zero?

Arduino is intended for people who have never programmed. People who have never programmed occasionally do unexpected, even strange, things.

It is important for the Arduino API (e.g. bit) to have well defined behaviour for those (all) occasions. The behaviour may not be useful but it should be well defined. For example, bit could return zero if the parameter is negative. That's not particularly useful but, if someone were to ask why bit(-5) does not seem to work, someone from the forum could easily respond, "well, you see, bit(negative) always returns zero because bit(negative) does not make sense."

The alternative requires the person providing support to first determine how bit(-5) behaves then decide if that behaviour matches the symptoms and then be in a position to help.

There's no such thing as _BV (-1)!

The problem is that, as far as the compiler is concerned, there is such a thing.

Personally, I would prefer bit(negative) / _BV(negative) to fail at compile time for constants and return zero for variables.

robtillaart:
Serial.println( 16 << -1 ); ==> 8 // so x << -y works like x >> y [UNO, 1.5.4]

so yes the macros is not complete… so the macro would become something like

#define _BV5(bit) (((bit) < -31) || ((bit) > 31)) ? (1ULL << (bit)) : ( 1UL << (bit)) )

the additional test would make it slower.
(quick test)
BV5: 1720 // same as 1ULL<< (bit)

So conclusion is that optimizing the BV macro for 64 bit only is possible if the range of bits is reduced to positive bits only .

thanks CB for this lesson !

Oops there was some stupid code in there … this one is slightly better…

#define _BV5(bit) (((bit) < 0) ? 0 : (((bit) > 31) ? (1ULL << (bit)) : ( 1UL << (bit)) ))