Go Down

Topic: isolating a digit (Read 1 time) previous topic - next topic

Paul Beaudet

I'm trying to efficiently isolate a digit. For example If have two values x=1106 and y = 1000, I'm looking the compare the differences in a granular way by place value like so

tens place x is what to y tens place
what == equal
ones place x is what to y ones place
what == grater by 6

This post will become ridiculous fast if I explain the why portion of the problem.
Needless to say the part I'm having a hard time with is isolating the digits to make the comparisons.
Paying more attention in high school math probably would have helped me here...
anyhow, here is what I looked at as a possible example http://www.daniweb.com/software-development/cpp/threads/151273/integer-place-values
and here is where i'm at with test code.
Code: [Select]
#include<avr/pgmspace.h>

prog_uint16_t preDefLayout[28] PROGMEM=
//just a bunch of unsigned ints
{
  1100,23000,2300,21000,1000,30000,3100,20001,32000,1500,3002,3000,6600,53000,
  60000,11000,15000,6000,3200,10,600,45000,20003,33000,62000,1001,1,10000
};

void setup()
{
  Serial.begin(9600);
  for(int i=0;i<28;i++)
  {
    unsigned int num = pgm_read_word_near(preDefLayout +i);
    for (int d=0; d<5; d++)
    {
      unsigned int num= compare%(10^d);
      unsigned int digit = a/(10^(d-1));
      Serial.print(" ");
      Serial.print(digit);
    }
    Serial.println();
  }
}

void loop()
{
}


PeterH


Code: [Select]

unsigned int num= compare%(10^d);



From the context I guess you expect 10^d to represent ten to the power d. Unfortunately ^ is the bitwise exclusive OR operator so is not going to do what you want. You could use pow() if you're determined to use this approach. However, it seems to me like a very strange algorithm and I can't imagine any problem that this approach is a sensible solution to.
I only provide help via the forum - please do not contact me for private consultancy.

spatula

Hi, beware of redefining the num variable within the for block:

Code: [Select]

    unsigned int num = pgm_read_word_near(preDefLayout +i);
    for (int d=0; d<5; d++)
    {
      unsigned int num= compare%(10^d);


My approach would be: get the highest power of 10 less than the numbers to compare, take the leftmost digits from the numbers using division, compare them, discard the leftmost digit using the modulus operation, get the next lower power of 10, repeat the cycle.

This results in making the comparison from left to right, as in your requirement.

Code: [Select]

 unsigned long first_number_to_compare, second_number_to_compare;
// here assign some value to the numbers

 unsigned long max_number_to_compare =
       first_number_to_compare > second_number_to_compare ?
               first_number_to_compare : second_number_to_compare;

// get the highest power of 10 that is less than the highest number
 unsigned long magnitude = 1;
 while (magnitude * 10 < max_number_to_compare)
 {
   magnitude *= 10;
 }

// compare the numbers starting from the leftmost digit
 while (magnitude > 0)
 {
   int first_digit_to_compare = first_number_to_compare / magnitude;
   int second_digit_to_compare = second_number_to_compare / magnitude;
// here compare the digits, print something

// cut off the leftmost digit
   first_number_to_compare %= magnitude;
   second_number_to_compare %= magnitude;
   magnitude /= 10;
}

Paul Beaudet

Quote
it seems to me like a very strange algorithm and I can't imagine any problem that this approach is a sensible solution to

Peter,
honestly it looked like gibberish to me too, I just trusted the person knew what they were doing, but I guess with my copy and paste methodology its a prime way to give a foolish example.

I found another example here http://www.cplusplus.com/forum/beginner/1326/ I'm just not sure how I can translate it to my problem.

spatula,
I'm still working out your solution in my head (l'm a bit of a dunce with math)  having a hard time visualizing how the modulus operation cuts off the leftmost digit.

marco_c

The modulus operator gives you the remainder when you divide the number by the divisor.

So 16 mod 10 = 6, because 10 fits into 16 once with remainder 6. 42 mod 10 = 2, etc
Arduino libraries http://arduinocode.codeplex.com
Parola hardware & library http://parola.codeplex.com

Paul Beaudet

thanks for for the explanation marco, I got that far, I was just not sure about
value=1106
value%=10
its this compound operator im not sure of, what is value equal to now?

I just tried a method comparing the ints a strings using itoa()...
that didn't go well  :smiley-eek: ... not even worth posting
itoa() seems to overflow when given an unsigned int with an unsigned'esk' value
and when blank spots in the buffer are compared they are random.

MichaelMeissner


thanks for for the explanation marco, I got that far, I was just not sure about
value=1106
value%=10
its this compound operator im not sure of, what is value equal to now?

The expression:

Code: [Select]

value %= 10;


is shorthand for:

Code: [Select]

value = value % 10;


The '%' operator is the modulus operator, which is equivalent to:

Code: [Select]

value = value - ((value / 10) * 10);


Note, integer division truncates, so if value was 1106 before the calculation, after 'value %= 10' it would hold 6.

So lets break it down:
1106 / 10 returns 110 (value / 10).
110 * 10 returns 1100 ((value / 10) * 10).
1106 - 1100 returns 6 (value - ((value / 10) * 10) or (value % 10).

spatula



thanks for for the explanation marco, I got that far, I was just not sure about
value=1106
value%=10
its this compound operator im not sure of, what is value equal to now?

The expression:

Code: [Select]

value %= 10;


is shorthand for:

Code: [Select]

value = value % 10;



@Paul, and this shorthand notation applies to other binary operators as well: +=, -=, *=, /=.
Where applicable, a op= b; is equivalent to a = a op b.

Thus, value = 1106 followed by value %= 10 will give you value = 6.
Now, allow me to expand a little bit: if value = 1106, what is value %= 1000

marco_c

#8
Apr 04, 2013, 08:06 am Last Edit: Apr 04, 2013, 08:09 am by marco_c Reason: 1
Quote
@Paul, and this shorthand notation applies to other binary operators as well: +=, -=, *=, /=.
Where applicable, a op= b; is equivalent to a = a op b.


Yes it does. It also applies to the logical operators |, &, ^, etc.

What's more, i++ is short for i+=1 which is short for i=i+1 :), but this form is only valud for ++ and --, not the other operators.
Arduino libraries http://arduinocode.codeplex.com
Parola hardware & library http://parola.codeplex.com

PeterH


I just trusted the person knew what they were doing


Do you know what they're trying to achieve? Unless they're trying to do something really weird, there may well be a simpler way to achieve it.
I only provide help via the forum - please do not contact me for private consultancy.

MarkT



I just trusted the person knew what they were doing


Do you know what they're trying to achieve? Unless they're trying to do something really weird, there may well be a simpler way to achieve it.


Absolutely, isolating a single digit is a very rare thing to want - usually you want to convert an integer to its
whole decimal expansion as a string in order to print it - in which case this is not the way to go about it, use
print or println.  So what is the overall thing that you're trying to achieve?
[ I won't respond to messages, use the forum please ]

Paul Beaudet

Quote
So what is the overall thing that you're trying to achieve?


I'm correcting for noisy input, each of the 5 digits represents a independent button values combined into one unsigned int earlier in the program. 1 for a press, 3 for a hold and so on. In that way 300 would represent a hold on the 3rd button and 10000 would represent a press of the first. To correct for noise I need to calculate the level of difference by input.

Implicitly I can tell that if 300 hasn't been assigned to a letter my user probably doesn't wan't the letter assigned to 10000 but If there is a letter assigned to 600 which is just a longer hold of button 3 maybe they are trying to go for the letter assigned to 600. Its just more likely that is what they were trying to do, considering the input they used had not been assigned.

Applying an explicit probability algorithm that acts in a similar manner is the next step, but first I have to get those digits alone to compare the individual place cases. The way I figure it I can build up differance counter(s) and pick the assignment the generates the least difference.   

There is likely a way to do this before the int is compiled, but it would conflict with another part of my program.
I'll post the link to the main body once I push the current commit, so that you folks have a better idea of the purpose of the project.

MichaelMeissner


Now, allow me to expand a little bit: if value = 1106, what is value %= 1000

Hmmm, a bit rusty on basic math?  The answer is 106.

Paul Beaudet

#13
Apr 04, 2013, 06:20 pm Last Edit: Apr 04, 2013, 06:24 pm by Paul Beaudet Reason: 1
here is the main body of code w/ out what I'm testing add for those that are curious

https://github.com/PaulBeaudet/JesterType/blob/master/JesterType.ino

Quote
Hmmm, a bit rusty on basic math?

Yes, I am!  but, I think spatula's intention was for me to think this out my self so I would get it

Basically 6106 or 5106 or 4106%= 1000 would be 106 thus the left most digit is being removed , and now I get it.
Thank you for bearing with me on that one, now I'll try to make a test program a report back.

Paul Beaudet

#14
Apr 04, 2013, 08:08 pm Last Edit: Apr 04, 2013, 08:11 pm by Paul Beaudet Reason: 1
I got things working  :D
sorry if abbreviating the variables bothers anyone. I always figure if its a problem I can just use the find & replace to fix.
Code: [Select]
#include<avr/pgmspace.h>

prog_uint16_t preDefLayout[28] PROGMEM=
//A to Z then yes then no.
{
 1100,23000,2300,21000,1000,30000,3100,20001,32000,1500,3002,3000,6600,53000,
 60000,11000,15000,6000,3200,10,600,45000,20003,33000,62000,1001,1,10000
};

unsigned int noise[5]= {3,6006,606,101,1010};
unsigned int chord;


void setup()
{
 Serial.begin(9600);
 for (int i=0;i<5;i++)
 {
   chord=filter(noise[i]);
   Serial.println(chord);
 }
}

void loop()
{
}

unsigned int filter(unsigned int noise)
{
 unsigned int correctToValue=9;
 //cant be nine so if it prints this something is wrong
 int lowPoint=15;
 for(int i=0;i<28;i++)
 {
   unsigned int largerNum;
   unsigned int compare = pgm_read_word_near(preDefLayout +i);
   unsigned int sComp=compare; //temporary second comparison
   unsigned int fComp=noise; //temp first comparison
   int pointCompare=0;
   if(fComp>sComp)
   {
     largerNum=fComp;
   }
   else
   {
     largerNum=sComp;
   };
   unsigned long mag= 1;
   while (mag*10<=largerNum)
   {
     mag*=10;
   }
   while(mag>0)
   {
     if(fComp>sComp)
       //overflow prevention
     {
       pointCompare+= fComp/mag - sComp/mag;
       Serial.print(fComp/mag - sComp/mag);
     }
     else
     {
       pointCompare+= sComp/mag-fComp/mag;
       Serial.print(sComp/mag-fComp/mag);
     }
     fComp%=mag;
     sComp%=mag;
     mag/=10;
     Serial.print(" ");
   }
   Serial.print(" total-");
   Serial.println(pointCompare);
   if(pointCompare<lowPoint)
     //filters to the path of least resistence
   {
     lowPoint=pointCompare;
     //set a new lowpoint
     correctToValue=compare;
     //remember the lowpoint value
   }
 }
 return correctToValue;
}


Thanks guys!

I have a couple thoughts about less iteration but I wonder If there are any inefficiencies that are obvious in what I wrote?

Go Up