Author Topic: how to break a value into 2 different variables  (Read 2975 times)

0 Members and 1 Guest are viewing this topic.

Offline little_carlosTopic starter

  • Regular Contributor
  • *
  • Posts: 133
how to break a value into 2 different variables
« on: May 28, 2016, 10:45:12 pm »
hello, i want to make a termometer using a pic16f886 using a lm35 and 2 7 segment displays
the problem i have is, how do i separate the dozens and the units of the value?
i know how to convert the decimal value to degrees, but how do i separate the units and dozens into 2 different variables?
i need this for the multiplexing part
 
 

Offline Aodhan145

  • Frequent Contributor
  • **
  • Posts: 403
  • Country: 00
Re: how to break a value into 2 different variables
« Reply #1 on: May 28, 2016, 10:57:10 pm »
if you need to split them in to tens and units you can use the modulo operator.
Code: [Select]
int originalNumber = 15;
int units = originalNumber % 10; // units = 5; as 5 is the remainder from 15 / 10;
int tens = (originalNumber - units) / 10;

« Last Edit: May 28, 2016, 10:58:57 pm by Aodhan145 »
 

Online IanB

  • Super Contributor
  • ***
  • Posts: 11885
  • Country: us
Re: how to break a value into 2 different variables
« Reply #2 on: May 28, 2016, 11:05:22 pm »
Another way is to work out the tens first:

Code: [Select]
int originalNumber = 15;
int tens = originalNumber / 10; // how many tens are in 15?
int units = originalNumber - 10 * tens; // the units are the remainder when you take the tens away

You can choose which way you find easiest to follow.
 

Offline Aodhan145

  • Frequent Contributor
  • **
  • Posts: 403
  • Country: 00
Re: how to break a value into 2 different variables
« Reply #3 on: May 28, 2016, 11:18:09 pm »
Another way is to work out the tens first:

Code: [Select]
int originalNumber = 15;
int tens = originalNumber / 10; // how many tens are in 15?
int units = originalNumber - 10 * tens; // the units are the remainder when you take the tens away

You can choose which way you find easiest to follow.

I thought that would be way faster than using modulo, there is absolutely no difference. That's surprising.
 

Offline little_carlosTopic starter

  • Regular Contributor
  • *
  • Posts: 133
Re: how to break a value into 2 different variables
« Reply #4 on: May 28, 2016, 11:55:28 pm »
if you need to split them in to tens and units you can use the modulo operator.
Code: [Select]
int originalNumber = 15;
int units = originalNumber % 10; // units = 5; as 5 is the remainder from 15 / 10;
int tens = (originalNumber - units) / 10;
it works with this number but with others give bad results
 

Online IanB

  • Super Contributor
  • ***
  • Posts: 11885
  • Country: us
Re: how to break a value into 2 different variables
« Reply #5 on: May 29, 2016, 12:33:05 am »
it works with this number but with others give bad results

For example?
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: how to break a value into 2 different variables
« Reply #6 on: May 29, 2016, 12:33:33 am »
Quote
how do i separate the units and dozens into 2 different variables?

division by subtraction.
================================
https://dannyelectronics.wordpress.com/
 

Offline macboy

  • Super Contributor
  • ***
  • Posts: 2254
  • Country: ca
Re: how to break a value into 2 different variables
« Reply #7 on: May 31, 2016, 04:05:17 pm »
Allow me to elaborate on dannyf's terse comment "division by subtraction".

Division is very very slow on microcontrollers because they only only have instructions that can do logic, add, and subtract, and sometimes multiply (Arduino ATMega328P can do multiply, thankfully). Division needs to be implemented by a complex, slow algorithm that the compiler inserts for you. So while doing a divide looks like a simple one-line statement in your source code, it can take hundreds of cycles to finish, not a single cycle like an add or subtract. That's why you want/need to avoid division. It is slow and adds code bloat.

Here is what you do instead:
1. if variable < 10 then goto 5 (finish)
2. subtract 10 from your variable
3. increment subtraction counter
4. goto 1.
5. finished.
subtraction counter is now the tens, and whatever remains in your original variable is the unit digit.

In C/C++ for Arduino it might look like this
Code: [Select]
uint8_t tens = 0;
while (myvariable > 9)
{
   myvariable =- 10;
   tens++;
}
For an input of myvariable=76, the loop will run 7 times, and at the end you will have tens=7 and myvariable=6

This is easily extended to handle hundreds, thousands, etc., it is small and fast, especially for small numbers.
For very large variables, you can do better, e.g. the "double dabble" algorithm. That is overkill for just 10's and 1's.
 
The following users thanked this post: little_carlos

Online IanB

  • Super Contributor
  • ***
  • Posts: 11885
  • Country: us
Re: how to break a value into 2 different variables
« Reply #8 on: May 31, 2016, 05:03:20 pm »
As a matter of curiosity, how sure are you that the manual repeated subtraction code will be faster than the automatically generated compiler code for division by 10?

It might be interesting to see what code the compiler actually generates for this operation.
 

Offline jnz

  • Frequent Contributor
  • **
  • Posts: 593
Re: how to break a value into 2 different variables
« Reply #9 on: May 31, 2016, 05:06:13 pm »
As a matter of curiosity, how sure are you that the manual repeated subtraction code will be faster than the automatically generated compiler code for division by 10?

It might be interesting to see what code the compiler actually generates for this operation.

Same here.

Although since all these chips have modulo, I'm not sure why that wouldn't just be used?
 

Offline DJohn

  • Regular Contributor
  • *
  • Posts: 103
  • Country: gb
Re: how to break a value into 2 different variables
« Reply #10 on: May 31, 2016, 05:16:36 pm »
I hadn't heard the name "double dabble" before.  But it's a good method that can be easily extended to all sorts of radix conversions, including mixed radix (for example, converting a number of seconds into weeks, days, hours, minutes, seconds).

The basic idea: you have a number in one representation (here it's binary) and you want to convert it into another (here it's decimal).  Start with the result initialised to 0.  Repeatedly halve the source while you double the result.  Every time you get a remainder from the halving, add it to the result.

For this particular case, that'll look something like
Code: [Select]
int units = 0;
int tens = 0;
int hundreds = 0;
while ( n > 0 ) {
  units = 2*units;
  tens = 2*tens;
  if ( units > 9 ) {
    units = units-10;
    tens = tens+1;
  }
  if ( tens > 9 ) {
    tens = tens-10;
    hundreds = hundreds+1;
  }
  if ( n%2 == 1 )
  {
    units = units + 1;
  }
  n = n >> 1;
}

The proper double dabble algorithm is a bit trickier: instead of the simple decimal doubling that I've got, it keeps two digits in each byte, and adds 3 to any half-byte that's greater than 4 before shifting it.  Here, we want to end up with the digits in separate variables, so I've just gone with the simple method.

Of course, with only two digits in your display, you can delete the bits dealing with the hundreds digit.


There's another trick that can sometimes be useful for calculating mods (it isn't here, since we need the result of the division as well, but I have encountered situations where it has helped).  Suppose the binary representation of our number is abcdefg.  That is, n = 64a + 32b + 16c + 8d + 4e + 2f + g.  We want n%10.  The rules of modular arithmetic mean that n%10 = (4a + 2b + 6c + 8d + 4e + 2f + g)%10 (I've just dropped the higher digits from each coefficient).  That gives us something with a much smaller range that can be calculated with a few shifts and adds.

4a + 2b + 6c + 8d + 4e + 2f + g won't be the final answer, because it can be up to 27.  But needs at most 2 subtractions to get it back to 0-9, instead of the 9 that repeated subtraction needs.  That gives us
Code: [Select]
c = n&0x10;
m = (n&0x60)>>4 + c>>2 + c>>3 + n&0x0f;
while ( m > 9 ) {
  m = m-10;
}

As I said, it doesn't help this problem, as we need the result of the division as well.


If your processor has a divide instruction, there's a chance it gives the remainder as well.  In that case, you can always say
Code: [Select]
  int units = n%10;
  int tens = n/10;
and hope the compiler does the right thing.
 

Offline macboy

  • Super Contributor
  • ***
  • Posts: 2254
  • Country: ca
Re: how to break a value into 2 different variables
« Reply #11 on: May 31, 2016, 05:41:47 pm »
As a matter of curiosity, how sure are you that the manual repeated subtraction code will be faster than the automatically generated compiler code for division by 10?

It might be interesting to see what code the compiler actually generates for this operation.

Same here.

Although since all these chips have modulo, I'm not sure why that wouldn't just be used?
PIC has modulo? No.  PIC16F866 has no modulus, no division, and no multiply instructions. Add, Subtract, logic, and shift are all it does.

The 'division by subtraction' is guaranteed small and fast for two reasons: you know you are limited to 2 digits (max 99) so the loop never runs more than 9 times. The compiler does not know such a limit and will generate code to handle up to the maximum value that can be stored (255). Also, the remainder (for the units digit) is generated as a byproduct of the divide operation. The compiler may calculate this separately. Indeed it would be interesting to examine the difference in terms of code size and speed, for different compilers, and different optimization levels.
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: how to break a value into 2 different variables
« Reply #12 on: May 31, 2016, 10:48:54 pm »
If I were to implement this, I would start from the biggest divider, obtain it's dividend and remainder, and apply the next biggest divider to the remainder successively.

For example, if you are dealing with uint32 types, I would start by dividing it by 10000, obtain the dividend and remainder. Then divide that remainder by 1000, obtain the next dividend and remainder; .....

Kind of opposite of what you would with a typical / and % approach.

Shouldn't be difficult to benchmark the two approaches against each other.
================================
https://dannyelectronics.wordpress.com/
 

Offline nctnico

  • Super Contributor
  • ***
  • Posts: 26906
  • Country: nl
    • NCT Developments
Re: how to break a value into 2 different variables
« Reply #13 on: May 31, 2016, 11:07:52 pm »
I'd use a simple lookup table. The range is limited and memory abundant.
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: how to break a value into 2 different variables
« Reply #14 on: June 01, 2016, 12:30:01 am »
Some rough numbers, on a PIC16F(886), counting cpu ticks from dividing 1 .. 64K:

1) using the % and / approach, it took 204 million cycles. flash usage: 160 bytes;

2) using the division by subtraction approach, as stated above, 34 million cycles. flash  usage: 104 bytes.

On a mcu with hardware dividers, those numbers may change, obviously.

edit: added flash usage.
« Last Edit: June 01, 2016, 12:33:05 am by dannyf »
================================
https://dannyelectronics.wordpress.com/
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: how to break a value into 2 different variables
« Reply #15 on: June 01, 2016, 12:52:55 am »
Quote
On a mcu with hardware dividers, those numbers may change, obviously.

Who wants to guess how those numbers would change, directionally, on a mcu with a hardware divider (say pic24 or pic32)?

:)
================================
https://dannyelectronics.wordpress.com/
 

Offline StillTrying

  • Super Contributor
  • ***
  • Posts: 2850
  • Country: se
  • Country: Broken Britain
Re: how to break a value into 2 different variables
« Reply #16 on: June 01, 2016, 01:06:25 am »
If the binary number is a single byte [0-255] I use this, I'd like to see a compiler beat it. :)

Code: [Select]
; Units[0-255] to Hundreds, Tens, Units
clrf Tens
clrf Hundreds
movlw .10 ; 10 in w

subwf Units,f ; sub 10 from Units
incf Tens,f ; inc Tens
btfsc STATUS,C
goto $-3 ; go back for another 10 from units

addwf Units,f ; add last 10 back in Units
decf Tens,f ; last 10 = 1 too many

subwf Tens,f ; sub 10 from Tens
btfss STATUS,C
goto $+3
incf Hundreds,f ; inc Hundreds
goto $-4

addwf Tens,f ; add last 10 back
; Fin Hundreds, Tens, Units

.  That took much longer than I thought it would.
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: how to break a value into 2 different variables
« Reply #17 on: June 01, 2016, 01:12:40 am »
Quote
I'd like to see a compiler beat it.

Depends on your definition of "beat it": the code I wrote earlier can run on PIC10/12/16/18/24/32, the entire AVR family, the entire ARM family, on a PC under DOS/Windows/Linux, on a VAX, etc., with zero modification.

Can yours beat that?

:)
================================
https://dannyelectronics.wordpress.com/
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: how to break a value into 2 different variables
« Reply #18 on: June 01, 2016, 01:27:15 am »
Quote
Some rough numbers, on a PIC16F(886), counting cpu ticks from dividing 1 .. 64K:

1) using the % and / approach, it took 204 million cycles. flash usage: 160 bytes;

2) using the division by subtraction approach, as stated above, 34 million cycles. flash  usage: 104 bytes.

Those numbers are for uint16_t types.

On uint8_t types, division from 1..256:

1) using the % and / approach, it took 451K cycles;
2) using the division by subtraction approach, as stated above, 34K cycles.

those cycles counts include converting the numbers into ascii char and stuff them into an array.

Can your assembly code beat that?

;)
================================
https://dannyelectronics.wordpress.com/
 

Offline StillTrying

  • Super Contributor
  • ***
  • Posts: 2850
  • Country: se
  • Country: Broken Britain
Re: how to break a value into 2 different variables
« Reply #19 on: June 01, 2016, 01:48:36 am »
Depends on your definition of "beat it"

Beat it as in getting the best out of a 35 instruction 16F, nowt else. :P
I've never used C on a PIC, so I don't know what the compilers come up with.
.  That took much longer than I thought it would.
 

Offline nctnico

  • Super Contributor
  • ***
  • Posts: 26906
  • Country: nl
    • NCT Developments
Re: how to break a value into 2 different variables
« Reply #20 on: June 01, 2016, 08:37:43 am »
Some rough numbers, on a PIC16F(886), counting cpu ticks from dividing 1 .. 64K:

1) using the % and / approach, it took 204 million cycles. flash usage: 160 bytes;

2) using the division by subtraction approach, as stated above, 34 million cycles. flash  usage: 104 bytes.

On a mcu with hardware dividers, those numbers may change, obviously.
Which compiler did you use? It shouldn't make such a difference when using fixed division factors!
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf