How to transform a multiple digit integer into multiple single digit integers?

Hi there, i have been trying recently to make a 4 digit 7 segment display work with a single 74hc595.
I need to do some multiplexing and stuff.

The problem is, I have some integer (ex. 9431) and I need to transform that into :

int A = 9;
int B = 4;
int C = 3;
int D = 1;

How?

Thanks!

int thousands;
int hundreds;
int tens;
int ones;

int val = 9431;
thousands = val / 1000;
val -= thousands * 1000;
hundreds = val / 100;
val -= hundreds * 100;
tens = val / 10;
val -= tens * 10;
ones = val;

Regards,
Ray L.

RayLivingston:

int thousands;

int hundreds;
int tens;
int ones;

int val = 9431;
thousands = val / 1000;
val -= thousands * 1000;
hundreds = val / 100;
val -= hundreds * 100;
tens = val / 10;
val -= tens * 10;
ones = val;




Regards,
Ray L.

if val = 9600, then the rounding would cause some problems here.

pozine:
if val = 9600, then the rounding would cause some problems here.

These are all integers. There is no rounding, only truncation.

9600 / 1000 = 9
9600 - 9000 = 600
600 / 100 = 6
600 - 600 = 0
0 / 10 = 0
0 / 1 = 0

Regards,
Ray L.

RayLivingston:
These are all integers. There is no rounding, only truncation.

9600 / 1000 = 9
9600 - 9000 = 600
600 / 100 = 6
600 - 600 = 0
0 / 10 = 0
0 / 1 = 0

Regards,
Ray L.

That's very useful to know. Thank you very much!

My personal favorite method is this...
Plus I believe it has less operations on it, so it may run faster...

int thousands, hundreds, tens, ones;
int x = 9431;
thousands = (x % 10000) / 1000;
hundreds = (x % 1000) / 100;
tens = (x % 100) / 10;
ones = (x % 10);

Convert the number into a char array (I think sprintf() does that) and then iterate across the array ?

...R

Plus I believe it has less operations on it, so it may run faster...

No, it won't. The % function is not fast.

PaulS:
No, it won't. The % function is not fast.

if you need optimal performance, checkout devmod10: it optimizes the divide by 10 and modulo 10 operation exactly for this purpose.

(warning not for the faint of heart :wink: see - divmod10() : a fast replacement for /10 and %10 (unsigned) - Libraries - Arduino Forum -

Ps991:
My personal favorite method is this...
Plus I believe it has less operations on it, so it may run faster...

int thousands, hundreds, tens, ones;

int x = 9431;
thousands = (x % 10000) / 1000;
hundreds = (x % 1000) / 100;
tens = (x % 100) / 10;
ones = (x % 10);

I'd be amazed if that was any faster. In fact, it could be a bit slower. '/' and '%' are both division operations, which are relatively slow. My code does a total of four such operations (plus three multiplies), while yours does seven divides. I would, therefore, expect mine to run as fast, and perhaps just a bit faster. Fewer lines does not necessarily mean the code will be either smaller, or faster.

Regards,
Ray L.

robtillaart:
if you need optimal performance, checkout devmod10: it optimizes the divide by 10 and modulo 10 operation exactly for this purpose.

(warning not for the faint of heart :wink: see - divmod10() : a fast replacement for /10 and %10 (unsigned) - Libraries - Arduino Forum -

Though that is for unsigned only...

Regards,
Ray L.

Though that is for unsigned only...

So you need to add a check to see if x < 0 ... and it will still be much faster

Ps991:
My personal favorite method is this...
Plus I believe it has less operations on it, so it may run faster...

Do not believe: test!

Modulo is surprisingly slow. It is about as slow as division in my tests. Which is to say, just terrible.

When I needed to do this conversion inside a motor-control loop, I found it was faster to subtract 1/10/100/1000 multiple times than to do any division. Divmod10 might be faster than my method but I haven't tested it as the subtraction method was fast enough for my purpose.

Code size may also be important for some situations. If you don't do any divisions in your program, the code is smaller because it doesn't compile the divide code. It is difficult to write a program big enough to get near the Arduino memory limit without using division anywhere so this is a rare case.

So.....um.....Im not going to say it

Ps991: 105192 uS per 1000 calls
RayLivingston: 49336 uS per 1000 calls

EDIT: test sketch in my next post

Im not going to say it

Or post the code you used. So, I'm not going to believe that those numbers are anything more than fiction.

int thousands, hundreds, tens, ones;
volatile int x = 9431; //prevent optimizations during compilation

void setup()
{
  Serial.begin(115200);
  long t = micros();
  for(int i = 0; i < 1000; ++i)
  {
    thousands = (x % 10000) / 1000;
    hundreds = (x % 1000) / 100;
    tens = (x % 100) / 10;
    ones = (x % 10);
  }
  t = micros() - t;
  Serial.printf("\nPs991: %ld uS per 1000 calls\n", t);

  t = micros();
  for(int i = 0; i < 1000; ++i)
  {
    thousands = x / 1000;
    x -= thousands * 1000;
    hundreds = x / 100;
    x -= hundreds * 100;
    tens = x / 10;
    x -= tens * 10;
    ones = x;
  }
  t = micros() - t;
  Serial.printf("RayLivingston: %ld uS per 1000 calls", t);
}

void loop(){}

Ps991:
So.....um.....Im not going to say it

Ps991: 105192 uS per 1000 calls

RayLivingston: 49336 uS per 1000 calls

I'm quite surprised it's that lop-sided. I figured mine would be a bit faster, but not 2X. The Arduino % code must be truly hideous, since it doesn't need to be much more than a very slightly modified divide.

Regards,
Ray L.

I tried this code out using divmod10 and it is really fast

21188 uS per 1000 calls

void divmod10(uint32_t in, uint32_t &div, uint8_t &mod) __attribute__((noinline));
void divmod10(uint32_t in, uint32_t &div, uint8_t &mod)
{
 //assumes that div/mod pointers arrive in r18:r19 and r20:r21 pairs (doesn't matter which way around)
 //and that in arrives in r22:r25 quad
  asm volatile(
   "movw r30, %2   \n\t"  //uint32_t* divPtr = &div;
   "movw r26, %1    \n\t" //uint32_t* modPtr = &mod;
   
   "mov   r0, %A0  \n\t"  //byte temp = in
   "movw r18, %A0  \n\t"  //uint32_t q = in;
   "movw r20, %C0  \n\t"  
   "ori  r18, 0x01 \n\t"  //q |= 1;
   
   "lsr  r25       \n\t"  //x = in >> 2 //note: x reuses registers of 'in', as 'in' was backed up in r0
   "ror  r24       \n\t"
   "ror  r23       \n\t"
   "ror  r22       \n\t"
   "lsr  r25       \n\t"  
   "ror  r24       \n\t"
   "ror  r23       \n\t"
   "ror  r22       \n\t"
   
   "sub  r18, r22  \n\t" //q = q - x;
   "sbc  r19, r23  \n\t"
   "sbc  r20, r24  \n\t"
   "sbc  r21, r25  \n\t"
   
   "movw r22, r18  \n\t" //x = q;
   "movw r24, r20  \n\t"
   "lsr  r25       \n\t" //x = x >> 4;
   "ror  r24       \n\t"
   "ror  r23       \n\t"
   "ror  r22       \n\t"
   "lsr  r25       \n\t"
   "ror  r24       \n\t"
   "ror  r23       \n\t"
   "ror  r22       \n\t"
   "lsr  r25       \n\t"
   "ror  r24       \n\t"
   "ror  r23       \n\t"
   "ror  r22       \n\t"
   "lsr  r25       \n\t"
   "ror  r24       \n\t"
   "ror  r23       \n\t"
   "ror  r22       \n\t"
   
   "add  r22, r18  \n\t" //x = x + q
   "adc  r23, r19  \n\t"
   "adc  r24, r20  \n\t"
   "adc  r25, r21  \n\t"
   
   "movw r18, r22  \n\t" //q = x
   "movw r20, r24  \n\t"
   "add  r18, r23  \n\t" //q = q + (x >> 8)
   "adc  r19, r24  \n\t"
   "adc  r20, r25  \n\t"
   "adc  r21, r1   \n\t"

   "movw r18, r20  \n\t" //q = q >> 16 
   "eor  r20, r20  \n\t"
   "eor  r21, r21  \n\t"
   "add  r18, r23  \n\t" //q = q + (x>>8)
   "adc  r19, r24  \n\t"
   "adc  r20, r25  \n\t"
   "adc  r21, r1   \n\t" //NOTE: r1 is a known 0.
   "add  r18, r22  \n\t" //q = q + x
   "adc  r19, r23  \n\t"
   "adc  r20, r24  \n\t"
   "adc  r21, r25  \n\t"
    
   "mov  r18, r19  \n\t" //q = q >> 8
   "mov  r19, r20  \n\t"
   "mov  r20, r21  \n\t"
   "eor  r21, r21  \n\t"
   "add  r18, r22  \n\t" //q = q + x
   "adc  r19, r23  \n\t"
   "adc  r20, r24  \n\t"
   "adc  r21, r25  \n\t"
   
   "andi r18, 0xF8 \n\t" //q = q & ~0x7
   
   "sub   r0, r18  \n\t" //in = in - q
   
   "lsr  r21       \n\t" //q = q >> 2 
   "ror  r20       \n\t"
   "ror  r19       \n\t"
   "ror  r18       \n\t"
   "lsr  r21       \n\t"
   "ror  r20       \n\t"
   "ror  r19       \n\t"
   "ror  r18       \n\t"
   
   "sub  r0, r18   \n\t" //in = in - q
   "st    X, r0    \n\t" //mod = in;
   
   "lsr  r21       \n\t" //q = q >> 1
   "ror  r20       \n\t"
   "ror  r19       \n\t"
   "ror  r18       \n\t"
   
   "st	    Z, r18  \n\t" //div = q
   "std  Z+1, r19  \n\t"
   "std  Z+2, r20  \n\t"
   "std  Z+3, r21  \n\t"
   
   : 
   : "r" (in), "r" (&mod), "r" (&div)
   : "r0", "r26", "r27", "r31", "r31"
 );
}

long t;

uint32_t thousands;
uint8_t hundreds, tens, ones;
uint32_t x = 9876;

void setup()
{
  Serial.begin(115200);
  
  t = micros();
  for(int i = 0; i < 1000; ++i)
  {
    divmod10(x, x, ones);                    //x = 987, ones = 6
    divmod10(x, x, tens);                    //x = 98, tens = 7
    divmod10(x, thousands, hundreds);        //thousands = 9, hundreds = 8
  }
  t = micros() - t;
  Serial.printf("%ld uS per 1000 calls\n", t);
  
  //I have no idea why this does not work, displays 9 0 8 7
  //Serial.printf("%ld uS per 1000 calls, %d %d %d %d\n", t, thousands, hundreds, tens, ones);
  
  //This works fine
  //Serial.println(thousands);
  //Serial.println(hundreds);
  //Serial.println(tens);
  //Serial.println(ones);
  
}

void loop(){}
  for(int i = 0; i < 1000; ++i)
  {
    thousands = (x % 10000) / 1000;
    hundreds = (x % 1000) / 100;
    tens = (x % 100) / 10;
    ones = (x % 10);
  }

Since you never do anything with the results, and none of the variables are volatile, the compiler apparently was smarter than you, and decided that doing this 1000 times was silly.

x was declared volatile, it did do it 1000 times, why else would it take 105ms, unless you are trying to say that the compiler deciding to do nothing takes 105ms which is silly.