how to break a value into 2 different variables - Page 1
Forum section: electronics / general

author: little_carlos
author_rank: Regular Contributor
timestamp: May 28, 2016, 10:45:12 pm
content: hello, i want to make a termometer using a pic16f886 using a lm35 and 2 7 segment displaysthe 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

author: Aodhan145
author_rank: Frequent Contributor
timestamp: May 28, 2016, 10:57:10 pm
content: if you need to split them in to tens and units you can use the modulo operator.Code:int originalNumber = 15;int units = originalNumber % 10; // units = 5; as 5 is the remainder from 15 / 10;int tens = (originalNumber - units) / 10;

author: IanB
author_rank: Super Contributor
timestamp: May 28, 2016, 11:05:22 pm
content: Another way is to work out the tens first:Code: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 awayYou can choose which way you find easiest to follow.

author: Aodhan145
author_rank: Frequent Contributor
timestamp: May 28, 2016, 11:18:09 pm
content: Quote from: IanB on May 28, 2016, 11:05:22 pmI thought that would be way faster than using modulo, there is absolutely no difference. That's surprising.

author: little_carlos
author_rank: Regular Contributor
timestamp: May 28, 2016, 11:55:28 pm
content: Quote from: Aodhan145 on May 28, 2016, 10:57:10 pmit works with this number but with others give bad results

author: IanB
author_rank: Super Contributor
timestamp: May 29, 2016, 12:33:05 am
content: Quote from: little_carlos on May 28, 2016, 11:55:28 pmFor example?

author: dannyf
author_rank: Super Contributor
timestamp: May 29, 2016, 12:33:33 am
content: Quotedivision by subtraction.

author: macboy
author_rank: Super Contributor
timestamp: May 31, 2016, 04:05:17 pm
content: Allow me to elaborate on dannyf's terse comment "division by subtraction".Division is veryveryslow 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 variable3. increment subtraction counter4. 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 thisCode:uint8_t tens = 0;while (myvariable > 9){myvariable =- 10;tens++;}For an input ofmyvariable=76, the loop will run 7 times, and at the end you will havetens=7andmyvariable=6This 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.

author: IanB
author_rank: Super Contributor
timestamp: May 31, 2016, 05:03:20 pm
content: 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.

author: jnz
author_rank: Frequent Contributor
timestamp: May 31, 2016, 05:06:13 pm
content: Quote from: IanB on May 31, 2016, 05:03:20 pmSame here.Although since all these chips have modulo, I'm not sure why that wouldn't just be used?

author: DJohn
author_rank: Regular Contributor
timestamp: May 31, 2016, 05:16:36 pm
content: 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 likeCode: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 usCode: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 sayCode:int units = n%10;int tens = n/10;and hope the compiler does the right thing.

author: macboy
author_rank: Super Contributor
timestamp: May 31, 2016, 05:41:47 pm
content: Quote from: jnz on May 31, 2016, 05:06:13 pmPIC 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.

author: dannyf
author_rank: Super Contributor
timestamp: May 31, 2016, 10:48:54 pm
content: 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.

author: nctnico
author_rank: Super Contributor
timestamp: May 31, 2016, 11:07:52 pm
content: I'd use a simple lookup table. The range is limited and memory abundant.

author: dannyf
author_rank: Super Contributor
timestamp: June 01, 2016, 12:30:01 am
content: 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.

author: dannyf
author_rank: Super Contributor
timestamp: June 01, 2016, 12:52:55 am
content: QuoteWho wants to guess how those numbers would change, directionally, on a mcu with a hardware divider (say pic24 or pic32)?

author: StillTrying
author_rank: Super Contributor
timestamp: June 01, 2016, 01:06:25 am
content: If the binary number is a single byte [0-255] I use this, I'd like to see a compiler beat it.Code:; Units[0-255] to Hundreds, Tens, UnitsclrfTensclrfHundredsmovlw.10; 10 in wsubwfUnits,f; sub 10 from UnitsincfTens,f; inc TensbtfscSTATUS,Cgoto$-3; go back for another 10 from unitsaddwfUnits,f; add last 10 back in UnitsdecfTens,f; last 10 = 1 too manysubwfTens,f; sub 10 from TensbtfssSTATUS,Cgoto$+3incfHundreds,f; inc Hundredsgoto$-4addwfTens,f; add last 10 back; Fin Hundreds, Tens, Units

author: dannyf
author_rank: Super Contributor
timestamp: June 01, 2016, 01:12:40 am
content: QuoteDepends 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?

author: dannyf
author_rank: Super Contributor
timestamp: June 01, 2016, 01:27:15 am
content: QuoteThose 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?

author: StillTrying
author_rank: Super Contributor
timestamp: June 01, 2016, 01:48:36 am
content: Quote from: dannyf on June 01, 2016, 01:12:40 amBeat it as in getting the best out of a 35 instruction 16F, nowt else.I've never used C on a PIC, so I don't know what the compilers come up with.

author: nctnico
author_rank: Super Contributor
timestamp: June 01, 2016, 08:37:43 am
content: Quote from: dannyf on June 01, 2016, 12:30:01 amWhich compiler did you use? It shouldn't make such a difference when using fixed division factors!