assigning a max bit value to an integer

Subject does not describe what I want do very well...

How do you approach the coding for a value that must roll over to 0 when it reaches a desired maximum or roll over to the maximum when reaching zero.

It just so happens that that the max I need is 1024, so a value limited to ten bits would roll over to 0 after 1023.

Setup: Rotary encoder, 1024ppr. Arduino Mega2560.

Current Code uses the Z pulse to reset the Encoder count to zero. The value obviously increments or decrements depending on direction of rotation. This leads to the obvious issue of negative numbers.

Any simple ways to get around this?

if you use a signed integer and after processing just mask the first 10 bits (or cloak the other 6)

int value = 0;
value = value & 0x3FF; // bitwise 'and' on 1023

of course this only works if the limit is exactly a number of bits. Otherwise just simply

if (value < 0) value = limit + value;  // edit: i had the sign wrong.
if (value > limit) value = value - limit;

Use an standard integer big enough to hold you max value ... in your case that's a unit16_t.

After incrementing or decrementing your value, do a bit-wise AND with 0x3FF.

Be careful using unsigned integers when you want the lowest value to be 0, this won't work:

  uint8_t someVariable;
  if (somevariable < 0) {
    someVariable = 200;
  }

if you want 0 to be the lowest value then you need a signed interger, otherwise it can never be less than 0.

  int16_t someVariable;
  if (somevariable < 0) {
    someVariable = 200;
  }

If your limit is not a power of 2:

value = (value + 1) % limit;

The '%' is the modulo operator. It calculates the remainder when dividing by the limit. For example if your limit is 360 then when dividing 359 by 360 the remainder is 359. When dividing 360 by 360 the remainder is 0.

If your limit is not a power of 2:
value = (value + 1) % limit;

actually, yes but like this: make sure that your variable is signed and at least capable of holding, 2 x limit + 1x max increment/decrementvalue = (value + limit ) % limit;

in your case that's a unit16_t.

since the sign-bit gets masked out a normal int would work as well.

EDIT: Nevermind, this doesn't work. The remarks about using signed integers are still true, though.

If you need a value from zero to n-1, you should:

ALWAYS use unsigned integers
use the modulus ('%') operator.

If you use signed integers, the result can have the same sign as the input!

If x is currently zero, and is a signed integer, then (x-1) % 360 could return -1 as a result.

If x is currently zero, and is an UNSIGNED integer, then (x-1) %360 will return 359 as a result, GUARANTEED.

SIGNED operands to modulus give a signed result that is IMPLEMENTATION DEFINED. Don't do it, that the path to madness.

If your 'n' is a power of two, you COULD use bit masking (which is likely faster than the modulus operator), but compilers are smart these days, so if you do (x-1) % 1024, the compiler will likely use bit masking for you. The modulus operator better describes what you are trying to do, so use that instead.

The problem with using signed integers is that the result doesn't have to be positive - the sign of the result is IMPLEMENTATION DEFINED for negative values.

If you are ALWAYS incrementing / decrementing by one, then the simple 'if' bracket probably works best.

x = {new value];
if x >= MAX x = 0;
if x <0 x = MAX-1;

If the amount you add / subtract is ALWAYS between -MAX and +MAX:

x = {new value}
if (x > MAX) x = x -MAX;
if (x < 0) x = x+ MAX;

If you need to add or subtract larger quantities, then:

x = {new value};
x = x % MAX;      //  will ALWAYS be between -MAX and + MAX, no matter the implementation.
if (x<0) x = x+ MAX;   //  x is now ALWAYS 0 to MAX-1.

Could it be a bit field application?

struct bfTest {
  unsigned int countEight: 3; // field is three bits wide
}someBits;

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

void loop() {
  delay(600);
  Serial.println(someBits.countEight--);
}

Use an unsigned int masked with 0x3FF (1023), if incrementing, the Z pulse would zero the counter at 1023 + 1, if decrementing, at 0 - 1, the counter would roll under to 65535, but with the mask you would see 1023, but the Z pulse SHOULD reset to zero when decremented from 1 to 0. Right? :confused:

unsigned int count = counter & 0x3FF;

Thanks for all the input. Will read through and report back. I have also considered the option of simply multiplying by -1 if the raw value is negative. The Z can be used to reset to 1 rather 0. So the count is between 1 and 1024.

Using an “if” statement is much faster and smaller than modulus.

westfw:
Using an “if” statement is much faster and smaller than modulus.

Depends entirely on which processor is running the code(*). Compilers are also smart and will often
optimize modulus by a power of two direct to a masking operation, same as they optimize
multiply and divide by powers of two (especially for unsigned).

(*) On a powerful CPU as in your laptop/computer branches are often slower than most instructions as
the instruction-prefetch cache gets invalidated, whereas arithmetic operations are superscalar and
overlapped with other instructions in time, costing nothing even!

Admittedly not many microcontrollers are that sophisticated.

It's pretty simple in my mind. If the counter runs from 0 to 2^N-1, then use a large enough unsigned integer type (i.e. uint16_t) and use bit-wise 'and' with 2^N-1 to mask off the upper bits after every arithmetic operation. This has the advantage of being explicitly clear what's happening. It also shows that you've thought about it and might know what you're doing.

westfw:
Using an “if” statement is much faster and smaller than modulus.

Doesn't matter how fast it is if it doesn't work. If (for example) you want 0->359 (i.e., modulo 360), then if you are adding values greater than 360, you'd need a loop to get back into range.

So, an if can work with the right pre-conditions. Modulus works all the time.

MHotchin:
Doesn't matter how fast it is if it doesn't work. If (for example) you want 0->359 (i.e., modulo 360), then if you are adding values greater than 360, you'd need a loop to get back into range.

So, an if can work with the right pre-conditions. Modulus works all the time.

This is simply not true. If you are worried about initial conditions you can put the test first:

if (index > max) index = 0;
index++;

In many cases, what you call "pre-conditions" are really just a normal, safe initialization of the index. That shouldn't be left to chance. Starting with an index out of range would normally be a sign of bad planning.

The modulus operator has a very large code size and speed overhead over an increment, test and reset.

"Pre-conditions" are not initialization. Pre-conditions are logical statements about current values that are required to be true for the code block to run correctly.

In your code, the 'pre-condition' is that the value only ever changes by one. If the code someone writes does not satisfy that condition, then your code doesn't work - in this case, because the pre-condition is embedded in the code, we would say that is the wrong code.

The 'if' statement variations all have pre-conditions, ranging from 'value only ever changes by one' to 'value only ever changes by at most MAX'.

The modulus version does not have those pre-conditions.

So, once you know your pre-conditions, then you can choose the right algorithm. Saying "Modulus is too expensive, never use it" is simply wrong, because you can't say that people's code will always satisfy the pre-conditions required to use something else.

For this particular case, modulus is overkill, but it succinctly encapsulates what the desired behavior is. "Premature optimization is the root of all evil" (Donald Knuth) - worrying about the efficiency of modulus only makes sense once you are sure it is a significant use of time in your program.

So, an if can work with the right pre-conditions. Modulus works all the time.

This is of course not entirely true. modulos have their limitation just as much, the total value should not exceed the size of the variable. The if statement does not deal with a value bigger than the total range but the variable size can be slightly smaller if the added (or subtracted) value is within the total range. Speed is a consideration, but overflow may be as well. I tend to use modulo because i need to know at what part of a cycle i am at and i don't want to bother about overflow issuesincycle = elapsedtime % cycle;and speed is of a lesser importance for me. If you want to make sure that a value can also be decreased, then you need to add the limit an additional time (the modulo of a negative value is not what you want)value = (value + limit) % limit;if your lower limit is not zero the whole thing gets way more complex though. Only when using a bit-mask should you use an unsigned variable if subtraction is possible (8-bit 0b10000001 = -1 after all, and with bit-masks 0b11111111 is the result we want after 0--; )
Choices, choices... many good options the OP started a nice discussion here.