Storing and retrieving large values and reducing code space.

Adiman

Member
Hi all,

I am using a Picaxe 20x2 and am trying to add multiple 16 bit values and have come up with the following code. I'd be interested to know if what I have come up with is a reasonable way of doing it or not, and if there is anyway I can improve this code to reduce program space consumed?

Aim:
1. Add multiple 16 bit values up to a approximate maximum of 65,535,000
2. Retrieve that value later on in a format I can read back into a single 16 bit variable by losing 3 digits, as accurately as possible, with the last digit rounded. For Example: 65,123,900 would be returned as 65,124

Notes:
Each value is initially calculated and stored in the variable 'Reading' and could be anywhere from 0-65535. (code excluded in this example as not pertinent)
Assume all variables are declared as word variables.

'The following code can store multiple 16 bit values up to a total of 4,294,901,760 using two word variables (ValueLow & ValueHigh) for storage. ValueHigh value of 1 would equal = 65535, ValueLow is simply the base value.

ValueLow = ValueLow + Reading
If ValueLow < Reading then {ValueHigh = ValueHigh + 1} ENDIF

'The following code restores a number up to approx 65,535,000, removing the last 3 digits and rounds the last digit for as much accuracy as possible.

FinalValue = 5 * ValueHigh / 10
FinalValue = ValueLow / 10 + FinalValue
FinalValue = 3 * ValueHigh + FinalValue / 10
TempValue = 5 * ValueHigh + FinalValue // 10 / 5
FinalValue = 5 * ValueHigh + FinalValue / 10 + TempValue
FinalValue = 5 * ValueHigh + FinalValue
FinalValue = 6 * ValueHigh * 10 + FinalValue
 
Last edited:

AllyCat

Senior Member
Hi,

It rather depends what other calculations may be needed and if you want to use a "custom" (specific) solution or "generic" program code. Personally, as soon as I need to handle numbers significantly above 65535 (16 bits), I just "pair up" words (High and Low) and use some fairly generic "double word" subroutines (or macros for greater speed, or if used only once). It's sometimes helpful to use w0 and/or w1 for this so that the bit(s) to "carry" (from one word to the next) are directly available. An advantage of this method is that you can develop/debug the code modules with the numbers initially symbolised as bytes and the PICaxe Editor will still "understand" the larger intermediate values (i.e. as words).

A search of the forum for "double word" should find some of my subroutines (particularly in the Code Snippets section). For the final scaling (e.g. division by 1000) I normally use my "31 / 15 bits division subroutine" which uses about 30 bytes of codespace (but may be rather slower). One way to "round" the result is to add half the divisor to the numerator, before the division.

Cheers, Alan.
 

hippy

Technical Support
Staff member
1. Add multiple 16 bit values up to a approximate maximum of 65,535,000
2. Retrieve that value later on in a format I can read back into a single 16 bit variable by losing 3 digits, as accurately as possible, with the last digit rounded.
I would be tempted to have a word variable which stores the last three decimal digits, 0-999, and another word variable which can store the leading five digits, 0-65535.

That way you don't have to do anything too complicated to keep track of the accumulated total, or for the final result ...

Code:
Symbol msd = w0 ; 0-65535
Symbol lsd = w1 ; 0-999
 
#Macro AddValue(val)
  msd = val /  1000 + msd
  lsd = val // 1000 + lsd
  msd = lsd /  1000 + msd
  lsd = lsd // 1000
#EndMacro

AddValues:
  AddValue( 12345 )
  AddValue( 54321 )
  AddValue( 56790 )

DoRounding:
  msd = lsd / 500 + msd

PrintResult:
  SerTxd( "Result = ", #msd, CR, LF )
12345 + 54321 + 56790 = 123456

That will show "Result = 123".

You can easily have it show to full accuracy ...

Code:
AddValues:
  AddValue( 12345 )
  AddValue( 54321 )
  AddValue( 56790 )

PrintResult:
  BinToAscii lsd, b11,b12,b13
  SerTxd( "Result = ", #msd, b11, b12, b13, CR, LF )
That will show "Result = 123456".
 

Adiman

Member
Hi,

A search of the forum for "double word" should find some of my subroutines (particularly in the Code Snippets section). For the final scaling (e.g. division by 1000) I normally use my "31 / 15 bits division subroutine" which uses about 30 bytes of codespace (but may be rather slower). One way to "round" the result is to add half the divisor to the numerator, before the division.

Cheers, Alan.
Your suggestion "Add half the divisor to the numerator" is an excellent low code space solution for rounding, how did I not think of that. suddenly my retrieval code loses a line, and saves 10 bytes code space:

FinalValue = 5 * ValueHigh / 10
FinalValue = ValueLow / 10 + FinalValue
FinalValue = 3 * ValueHigh + FinalValue / 10
FinalValue = 5 * ValueHigh + FinalValue + 5 / 10
FinalValue = 5 * ValueHigh + FinalValue
FinalValue = 6 * ValueHigh * 10 + FinalValue

As to your other suggestions on your subroutines, it's late and I don't think I can quite wrap my head around it just now, but i'll be sure to investigate further.

What do you think of my code to add Readings, is this an efficient solution? It will call this subroutine as many times as it needs to to store many 16 bit readings (by addition) in a high and low word:

ValueLow = ValueLow + Reading
If ValueLow < Reading then {ValueHigh = ValueHigh + 1} ENDIF

Cheers
 

hippy

Technical Support
Staff member
My post #3 was probably posted while you were writing your reply. Might be worth having a look at that.

It uses the same technique as using double words with just a more limited range to the lower word but has the advantage that there's no complexity in determining what the divide by 1000 or rounding should be - One cannot just look at the lower word when trying to figure those out, and that gets quite complicated!
 

Adiman

Member
I would be tempted to have a word variable which stores the last three decimal digits, 0-999, and another word variable which can store the leading five digits, 0-65535.

That way you don't have to do anything too complicated to keep track of the accumulated total, or for the final result ...

Code:
Symbol msd = w0 ; 0-65535
Symbol lsd = w1 ; 0-999
 
#Macro AddValue(val)
  msd = val /  1000 + msd
  lsd = val // 1000 + lsd
  msd = lsd /  1000 + msd
  lsd = lsd // 1000
#EndMacro

AddValues:
  AddValue( 12345 )
  AddValue( 54321 )
  AddValue( 56790 )

DoRounding:
  msd = lsd / 500 + msd

PrintResult:
  SerTxd( "Result = ", #msd, CR, LF )
Thanks Hippy, This is an interesting solution also, a little heavy on the program space to store the number, but so simple to retrieve as you have basically made it so the 'High' variable is the value I would want without needing any further calculations.
 

hippy

Technical Support
Staff member
This is an interesting solution also, a little heavy on the program space to store the number
You can reduce the memory by replacing the macro with a GOSUB. It shouldn't use more program memory than any other way of doing it ...

Code:
Symbol msd     = w0 ; 0-65535
Symbol lsd     = w1 ; 0-999
Symbol reading = w2
 
AddValues:
  reading = 12345 : Gosub AddReading
  reading = 54321 : Gosub AddReading
  reading = 56790 : Gosub AddReading

PrintResult:
  BinToAscii lsd, b11,b12,b13
  SerTxd( "Result = ", #msd, b11, b12, b13, CR, LF )
  End

AddReading:
  msd = reading /  1000 + msd
  lsd = reading // 1000 + lsd
  msd = lsd     /  1000 + msd
  lsd = lsd     // 1000
  Return
The 'reading = ... : Gosub AddReading" could be handled within a macro as before.
 

Jeremy Leach

Senior Member
If I've got the right end of the stick ... think I'd use 3 bytes to store the sum value accurately then retrieve a Word approximation by bit-shifting.


You've said the values you are adding are Words. If you add two Words on a PICAXE you don't see any overflow (if there is any) however you can tell if there has been overflow simply by comparing the result to the number you started with.


EXAMPLE 1 :
65512 + 50 = 65562. This addition of two Words (65512 and 50) using PICAXE arithmetic would give a result of 27 (65562 - 65535). PICAXE arithmetic works in Words and if you roll over the 65535 boundary you start from 0 again. I think of it like a wheel.


So, in this example 27 is LESS than 65512 so there HAS been overflow.


EXAMPLE 2:
100 + 50 = 150. Here the result (150) is GREATER than 100. No overflow has occurred.


So I'd have a sum value using two variables (3 bytes) B1 and W1, where B1 is an 'overflow' byte variable and W1 is a Word variable. This would let you add up to 255 Words together. (I don't think you said how many Words you actually wanted to add up)


Adding would simply be : ResultWord = W1 + WordToAdd
Then checking if there has been overflow (if ResultWord < W1 then there has) in which case you'd increment B1.


To retrieve a Word approximation of the 3 byte value you could simply combine B1 and the most significant byte of W1 into a Word.

Hope this makes sense !
 
Top