Is there a faster and more elegant way to "find which bit is high"? Or more accurately "find the first high bit" as after one is found, it exits, which is fine.
byte test (byte someRandomByte)
{
for (byte b = 0; b < 8; b++)
{
if (bitRead(someRandomByte, b))
return b;
}
return 255;//or whatever
}
if (someRandomByte > 127) return (7); // bit 7 is set
if (someRandomByte > 63) return (6); // bit 6 is set
if (someRandomByte > 31) return (5); // bit 5 is set
if (someRandomByte > 15) return (4); // bit 4 is set
if (someRandomByte > 7) return (3); // bit 3 is set
if (someRandomByte > 3) return (2); // bit 2 is set
if (someRandomByte > 1) return (1); // bit 1 is set
if (someRandomByte > 0) return (0); // bit 0 is set
If you truly want fast, and damn the cost, a 256-case switch statement is likely as fast as it gets. But a more reasonable approach with reasonable speed and size would be a binary search:
byte test (byte someRandomByte)
{
byte result = 0;
if (someRandomByte > 15)
{
result = 4;
xsomeRandomByte >>= 4;
}
if (someRandomByte > 3)
{
result += 2;
someRandomByte >>= 2;
}
if (someRandomByte)
result++;
return result;
}
There's some logic errors in Anders53's code, but the if-statements are far faster than the for-loop. Assuming 20microseconds is really too long for you to wait.
if-statements: 4 micros, 0
for loop: 20 micros, 7
tested using this sketch
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
Serial.print("if-statements: ");
Serial.flush();
unsigned long microsBefore;
unsigned long microsAfter;
int val;
microsBefore = micros();
val = ifStatements(0x01);
microsAfter = micros();
Serial.print(microsAfter - microsBefore);
Serial.print(" micros, ");
Serial.println(val);
Serial.flush();
Serial.print("for loop: ");
Serial.flush();
microsBefore = micros();
val = forLoopTest(0x80);
microsAfter = micros();
Serial.print(microsAfter - microsBefore);
Serial.print(" micros, ");
Serial.println(val);
Serial.flush();
while(1);
}
int ifStatements(byte someRandomByte) {
if (someRandomByte > 127) return 7; // bit 7 is set
if (someRandomByte > 63) return 6; // bit 6 is set
if (someRandomByte > 31) return 5; // bit 5 is set
if (someRandomByte > 15) return 4; // bit 4 is set
if (someRandomByte > 7) return 3; // bit 3 is set
if (someRandomByte > 3) return 2; // bit 2 is set
if (someRandomByte > 1) return 1; // bit 1 is set
if (someRandomByte > 0) return 0; // bit 0 is set
return 0;
}
int forLoopTest (byte someRandomByte)
{
for (byte b = 0; b < 8; b++)
{
if (bitRead(someRandomByte, b))
return b;
}
return 255;//or whatever
}
void loop() {
// put your main code here, to run repeatedly:
}
Not code, just 8 contigous if statements to demonstrate the idea, which you read loud and clear 8)
Assuming you would average 4 if statements to find the high bit, you would have an average time lapse of just 2 µS to run through it.
But it doesn't find the first high bit, it finds a single bit.
If you want to do it by comparison, which does meet the criterion, you have to decide whether you want to have a search that is balanced, or unbalanced. If you start looking "from the top" you will quickly find high values, but low values can take up to the maximum number of comparisons. If you want a balanced search, you can find it in a fixed number(3 in this case) of comparisons, by first testing whether it is greater than half the value, then split again by half of that and so on...
Thanks guys, that answers everything for me. I wasn't sure if there was some basic bit math that would simply calculate it somehow instead of searching.
If you don't mind searching from the least-significant end, try the standard library function ffs(). It's likely to be faster and/or smaller than anything you or I would write in C++.
The GNU compiler has a builtin macro for counting bits: __builtin_popcount (unsigned int); it is based on an old bit hack, invented by MIT in the early 1970s; if you want the lowest one bit in an 8 bit int, you can do the following:
int lowest(uint8_t b) {
if (!b) return 0; // no one bit here ...
b^= b-1; // contains n consecutive one bits where n is the bit position we want;
// count the bits
b= ((b>>1)&0x55)+(b&0x55);
b= ((b>>2)&0x33)+(b&0x33);
b= ((b>>4)&0x0F)+(b&0x0F);
return b;
}
This little method doesn't buy you much, but it gets better for 16 or 32 bit words (one or two lines need to be added).
gardner:
A 256 element lookup table in progmem would solve the problem in ~3 instructions.
The suggested set of 8 tests above, in itself generates 87 bytes of code, so that's 87 bytes gone anyway. The lookup table would take 256 bytes, but would certainly be the fastest. Plus it's more general. You can look up any fact about a byte with a lookup table.
In fact I believe the standard library functions isdigit, isxdigit and so on, share a 256-byte lookup table. One bit says if it is a digit, another if it is a hex digit, another if it is alpha, another if it is a space, and so on.
These, as presented, fail validation...
Original post
Reply #3
Reply #8
Reply #12
Reply #14
I modified these to pass validation...
Original post
Reply #14
Several fail when the parameter is zero which was not included in the validation. I modified the code in Reply #2 to return 255 when the parameter is zero.
Array lookup from SRAM is fastest at an average of 0.377µs.
Array lookup from Flash is second at an average of 0.440µs.
The (modified) code in Reply #2 is third at an average of 1.193µs.
static byte Test2F( byte someRandomByte )
{
if (someRandomByte > 127) return (7); // bit 7 is set
if (someRandomByte > 63) return (6); // bit 6 is set
if (someRandomByte > 31) return (5); // bit 5 is set
if (someRandomByte > 15) return (4); // bit 4 is set
if (someRandomByte > 7) return (3); // bit 3 is set
if (someRandomByte > 3) return (2); // bit 2 is set
if (someRandomByte > 1) return (1); // bit 1 is set
if (someRandomByte > 0) return (0); // bit 0 is set
return( 255 );
}
[quote author=Nick Gammon date=1428648012 link=msg=2180553]
In fact I believe the standard library functions isdigit, isxdigit and so on, share a 256-byte lookup table. One bit says if it is a digit, another if it is a hex digit, another if it is alpha, another if it is a space, and so on.[/quote]
avr-libc-1.8.1/libc/stdlib/ctype.S looks to be doing things the hard way. It must be more compact than using the table that would be used in most non-memory-constrained platforms.. Here's an example: