Most efficient way to nest multiple decision statements

I’m writing some code for a DMX receiver.

I need a set of decisions, I would normally use If statements, like this
Assuming variable val is the DMX channel value (10-254)

If val < 10 do xxx
Else if val >=11 and val < 20 do yyy
Else if val >=21 and val < 30 do aaa
Else if val >=31 and val < 40 do bbb
Else do ccc

Is there a simpler way to structure this?
There could be 15 or 20 ranges I want to take actions on.

Basically setting the DMX channel value will then activate a routine to do a certain sequence on the pixel light strip. Each range activated a different routine.

It is sometimes called the sifting rocks construction.

Suppose you have a load of rocks that needs to be sifted.
The coarse sieve for big rocks is on top. Below that a finer sieve and the bottom sieve is the finest and lets only the remaining smallest pebbles through.

if( i > 60)
{
  Serial.println( "Big rocks, more than 60 kg");
}
else if( i > 50)
{
  Serial.println( "Normal rocks, between 50 en 60 kg");
}
else if( i > 40)
{
  Serial.println( "Small rocks, between 40 and 50 kg");
}
else
{
  Serial.println( "Pebbles, less than 40 kg");
}

Once the big rocks above 60kg have been filtered out, you can test with "if ( i > 50 )" and be sure that they do not contain the big rock above 60kg.

If you want to test for 50 (inclusive) up to 60 (not inclusive) then you have to change the '>' to '>='.

Arduino-C/C++ appears to support case ranges like

  switch (val) {
  case 0 ... 9 :
    Serial.println( "tiny rocks...");
    break;
    
  case 10 ... 20 :
    Serial.println( "Elvis rocks...");
    break;
    
  default :
    Serial.println("I prefer jazz...");
    break;

  }

You get the idea. "..." is the range thing. I've only tried it with numbers, so.

I was surprised, too.

More here Using range in switch case in C/C++ - GeeksforGeeks

a7

The sorting rocks is easy to understand, and slightly more efficient than checking both vales in the range as you were doing (as you only need 1 comparison per if).

Here are some other approaches that may also be appropriate in some situations, and also may be more efficient:

Divide the value by some shared common constant, if there is one. In your case 10. Then you only have to check for equality with a single value instead of a range. 0, 1, 2, 3 in your example.

Do a binary chop:

if (value < 20) {
  if (value < 10) {
    xxx
   } else {
    yyy
   }
} else if (value < 40) {
  if (value < 30) {
    aaa
  } else {
    bbb
   }
} else {
  ccc
}

The idea of this approach is that every code path has roughly the same number of “if” statements that need to be checked, and so roughly constant execution time.

The way you have it coded the xxx code path only requires 1 comparison, whereas ccc code path needs 7. My code above xxx, yyy and ccc require only 2 checks, and aaa and bbb require 3.

Good compilers are usually capable of having a reasonable stab at implementing one of the above strategies for a case statement where appropriate, or will use a jump table for contiguous ranges.

If (val < 10)
{
   do xxx
}
else (if < 20) 
{
  do yyy
}
else (if < 30) 
{
  do aaa
}
else (if < 40) 
{
  do bbb
}
else 
{
  ccc
}

This is as simple as it gets.

Regards,
Ray L.

@alto777, the GCC compiler gives a warning for the switch-case range, because it is an extension for C++ and not part of the language.

[EDIT]The range might be valid now, see below.

Thank you all for your suggestions, I will give some of them a go today :slight_smile:

Koepel:
@alto777, the GCC compiler gives a warning for the switch-case range, because it is an extension for C++ and not part of the language.

I know that you know a lot more about the compiler than I do but can you expand a little?

When I compile below, I don't get warnings (and yes, compiler warnings set to all).

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  static byte val;

  switch (val++) {
    case 0 ... 9 :
      Serial.println( "tiny rocks...");
      break;

    case 10 ... 20 :
      Serial.println( "Elvis rocks...");
      break;

    default :
      Serial.println("I prefer jazz...");
      break;

  }
}

@Blue407, what RayLivingston is doing is the same thing. Both are valid and both are called: the sifting rocks.

Once the values less than 10 are filtered out, you can be sure that the rest is 10 or above. So you can continue in the same way.

Instead of sieves on top of each other, you can see it as a water stream with rocks. The sieves are in the bottom and the water stream with rocks passes over each sieve.

First a fine sieve, where every rock below 10kg falls through. The bigger rocks flow over that sieve.
Then a courser sieve, where rocks below 20kg fall through.
The biggest rocks have not fallen through any of the sieves, that is the remaining "else" at the end.

@sterretje, the warning is gone !
I use myself in the isDstEurope() function: Het Nederlandstalig Arduino forum - Bekijk onderwerp - klok datum met dag aanduiding.
But I always got a warning.
Your sketch does not give a warning and I compiled my clock project with my isDstEurope() and no warning there as well :smiley:

I've gone with the 'case' solution as it looks very neat when formatted.

I have successfully modified it this morning and its all working perfectly.
I can now send different DMX channel values from Freestyler which will initiate a number of different WS2811 pixel sequences.

It works very well. This is for some large (over 5ft) Marquee Letters for my wedding. I'm setting up the disco lighting and the letters will be part of the show. White during the day, but with funky patterns etc for the evening.

Thank you to everyone who has contributed.

I'm not sure I agree 100% with the example of sieving rocks.

For program efficiency with a series of interdependent tests the first test should be the one that is most likely to eliminate the need for other tests.

For example if I wanted to sort women according to their heights from a group that contains men and women I would first test if the person is a woman because, if not, there is no need to test for height.

...R

Blue407:
I've gone with the 'case' solution as it looks very neat when formatted.

...

I have to say when I first saw the range thing I didna like it. But I think this is a good place to use it. I wonder what the compiler makes of it, but don't care.

BTW I point out that the parser needs white space one both sides of the "..." thing.

Also thanks @sterretje for folding my snippet into a complete demonstration. I did say I tried it, by that I would always hope a person means a) wrote some code b) compiled it and c) observed the desired results.

This is an extension to C/C++ AFAIK, so I do wonder about the lack of warning for portability I guess at least. I tend to leave warnings turned down, er, somewhat, until I have a trouble I am not understanding.

a7

Robin2:
For program efficiency with a series of interdependent tests the first test should be the one that is most likely to eliminate the need for other tests

Plurally, the first tests should be the ones... The binary search hopes that the input data is random, the sieve hopes that the input data is skewed to large values. You could also make an "upside down" sieve that eliminates values starting from zero, it would be like "panning for gold" I guess... wash away bigger and bigger rocks until only the biggest are left. It would hope that the input data is skewed towards lower values.

aarg:
Plurally, the first tests should be the ones... The binary search hopes that the input data is random, the sieve hopes that the input data is skewed to large values. You could also make an "upside down" sieve that eliminates values starting from zero, it would be like "panning for gold" I guess... wash away bigger and bigger rocks until only the biggest are left. It would hope that the input data is skewed towards lower values.

I think that can be summarised as "an efficient program must take account of the nature of the data that is being tested"

...R

In my example, its random.

It all depends on which routines I want to kick off from the DMX controller.

I tried to put the most likely towards the top, for example all white, all red, all green, all blue. Then the more complicated routines afterwards.

Thanks again, I'm very pleased with the responses and the result.