November 21, 2024, 07:40:54 AM

News:

Got VSA?  Want to use your Prop-SX?  Now you can!  See the VSA section of the Library forum for Prop-SX code that works with VSA.


Useful PBASIC and SX/B Math Tricks

Started by JonnyMac, March 07, 2007, 08:52:54 AM

Previous topic - Next topic

JonnyMac

March 07, 2007, 08:52:54 AM Last Edit: April 30, 2008, 05:12:57 PM by JonnyMac
When it comes right down to it, programming is a numbers game; a program ends up being the shuffle of numbers in a specific order that give a specific result.  To that end, understanding few interesting math tricks will help your programs.

Masking

Masking is the process of keeping part of number and dumping off the other parts.  Say, for example, that you have a random value (we'll call it lottery) and you want to create a new value that has a range from zero to seven.  One method is masking with the & (bit-wise AND) operator.  The rule for & goes like this: If both bits are 1, the output is 1, otherwise the output is zero.  Here's a truth table:

0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1

When we apply & to numbers each bit is individually tested.  To limit a value between 0 and 7 we could and the starting value (lottery) with %111 (decimal 7).  Here's what it looks like, bit for bit:

%1010010010011001 - lottery
%0000000000000111 - mask
-------------------------
%0000000000000001 - result

In code we would do this:

  result = lottery & %111

If you study this a bit, you'll see that the highest possible result value is %111 (7) and that would be when the lower three bits of lottery are set.  Any other bit combination in lottery will result in a value less than 7 (%111). 

Masking is handy for stripping away unwanted bits in a value.


Modulus

The // (modulus) operator returns the remainder of a division.  Microcontrollers do integer division, so this is useful in general math operations.  We can use it to limit a value to a specific range.  As above, we'll use // to create a 0 to 7 value from a random number -- here's how:

  result = lottery // 8

Why eight as the divisor?  Remember, the modulus operator returns the remainder of a division, and that will always be between zero and the divisor-1.  We can also use create a value from some desired x to some desired y.  First, take the max value (y) and subtract the min value (x); this is the span.  Add one to the span and use this general formula:

  result = value // span + min

Let's look at a real example.  Let's say we have a random value and we want to create a delay from it that can be 25 to 100 milliseconds.  The span is 100 - 25 + 1.  The code becomes:

  delay = lottery // 76 + 25

The "// 76" part of the equation will return a value between zero and 75; this added to 25 (min) gives us the output range of 25 to 100.


** Operator  (Multiply High)

While masking with & and the modulus operator are available in many languages, the ** (multiply high) operator is special to PBASIC and SX/B.  The ** operator returns the high word (16-bit value) or a 32-bit multiplication result.  From a practical standpoint, using ** is like multiplying by a fractional value, less than 1.0, expressed in units of 1/65536.  This is a convenient operator for scaling values without attempt to do a multiply then divide.  For example, we can multiply a value by 0.75 by doing this:

  result = result * 3 / 4

The problem with this strategy is that if result is too big, we can get what's called a rollover error.  As stated earlier, ** uses a 32-bit internal result so this is not a problem.  To use ** in the equation above we first multiply our fractional value, 0.75, by 65336; we get 49152.  The code becomes:

  result = result ** 49152

.. and there is no danger of rollover error.  This operator is really good for scaling values by a given percentage -- so long as that percentage is less than 100.


*/ Operator  (Multiply Middle) -- Not Available on the Prop-1

The */ (Multiply Middle) operator works very much like **, but it returns the middle 16 bits of a 32-bit result; the effect is like multiplying by a fractional value between 0 and 255.999 that is expressed in units of 1/256.  Let's say we have a sensor reading that we need to scale by 7.352; we can do that with */.  We start by taking the fractional value, 7.352, and multiplying it by 256; the result is 1882.  The code becomes:

  sensor = rawSensor */ 1882

For those that are comfortable with hexadecimal numbers, you can convert the divisor into hex to make the code a bit more obvious.  The hex version of 1882 is $075A; notice how the upper byte ($07) represents the whole portion of the fractional value?  In my listings you'll see it like this:

  sensor = rawSensor */ $0752
Jon McPhalen
EFX-TEK Hollywood Office