PDA

View Full Version : Seeking the lowdown on masking functions



C-Dawg
01-10-2007, 06:32 PM
The data fields of FFCs only accept 8 user values. This is fine for most circumstances, and it's probably not worth increasing the number of data fields. However, in some cases, such as where one FFC script controls dozens of other FFCs, it might be helpful to be able to pass more values to a script.

The best way to do this, it seems to me, is to let the user enter a long string of two-digit ints in a single data field and then pull each two digits apart using a mask. So, for instance, I pass a script 1122334455 as D0, but the script then creates five different variables with the value 11, 22, 33, 44, and 55.

Problem is, I'm not familiar with masking functions. Anyone able to explain how they're implemented?

itsyfox
01-11-2007, 12:36 AM
I know better how to do it with strings than numbers, but I can see a mathematical way to do it with numbers.

So , say you want the values 1, 12, 123, 6, and 54. Your max place is hundreds (123), but we'll be dividing, so you actually need to use 1000 for place_increment (because 54/1000 = 0.054), and you'd pad any numbers that didn't go up to the hundreds place with zeros. Because of this, it's possible for leading zeros in front of the first number to be stripped, so either use floats (I don't know how far they can go), or put some number in the very front that will be ignored, like just 1.

Your number might look like: 1001012123006054.

So now, you have to get each of those numbers out.


last_value = D#_value - (floor(D#_value / place_increment)) * place_increment)

Here's what this will do. For the above example, your place increment is 1000.

The first thing you're doing (the innermost parenthesis) is grabbing the next lowest integer to D#_value / 1000, which just moves the 54 3 places: 1001012123006.054 and then removes the 54: 1001012123006.

Now, so your subtraction will work properly, you have to multiply the new answer by your place_increment again, and get: 100101212300600.

Finally, you subtract this answer from the original answer.

1001012123006054 - 100101212300600

And you get 054, which simply loses the extra zero and becomes 54.

You can then repeat this process in a loop as many times as you need to to get to the #th number. At the end of your loop, make sure to change d_val to floor(d_val/1000), because you're going to want to still be able to use 1000 as place_increment the next round.

In the end, the function should look something like this:



int maskint(int d_val, int end_place, int place_increment) {
int place = 1;
place_increment = Pow(10, place_increment);
int last_int = 0;
while(place < end_place) {
last_val = d_val - (Floor(d_val / place_increment) * place_increment);
d_val = Floor(d_val / place_increment);
place += 1;
}
return last_val;
}


The above code changes place_increment to just accept how many places to move each time, and turns that into the right number by using place_increment = 10^place_increment.

It could still use some error checking, for instance, if the end_place you return is longer than the number of places at place_increment that are available, your program will start returning 0, or maybe crash.

You could probably fix this by, after the code that turns place_increment into a power of 10, checking that floor(d_val / Pow(place_increment, end_place)) > 0. There's still not much you could do besides return 0 and exit; since you can't print warning/fail messages to the user or Allegro.log yet, as far as I know. But it'll at least prevent a crash, if one might occur.

Hope this works, and hope this code works!

I saw the concept for the math long before I got the exact equasion worked out, so I *really* hope it works like it should. I tested the math on a graphing calculator, step by step doing what the program would do and then putting it in with X as d_val into the graphing system and looking at the values on a table. It seemed to work, so I just hope it does. ^_^

EDIT: Alright...upon testing, I discovered that its truncation always goes four digits on either side.

1234.1234

That's as far as you can go. Sticking with double-digits, you could max 4 numbers out of a single mask this way. And with single you could get 7, or 8 if your first digit is greater than 0.

So that DOES offer a lot of single-digit values to pass, either 64 or 56, and a total of either 24 or 32 with double and then 24 with triple. So still not too bad.

EDIT #2:

Updated the code so it works properly with 1234.1234 format numbers. Also removed the while loop.
For some reason I had to separate the operations on last_val; when I didn't, it was doing funny things. Now it's working perfectly, and returns an integer (though with trailing decimal zeros).



int maskint(int d_val, int end_place, int place_increment) {
place_increment = Pow(10, (place_increment * end_place));
int last_val = d_val / place_increment;
last_val = last_val * place_increment;
last_val = d_val - last_val;
last_val = last_val * 100 * 100;
return last_val;
}


So it's basically the same function as above, except:

-place_increment is now set to the EXACT place of the value you're looking for, so when you divide, it'll truncate all the way the first time and not require a loop;
-the equasion that sets last_val has been split up over several operations, since the program did funny math when I left the equasion intact;
-and at the end, last_val is multiplied by 100 twice. This is so it doesn't truncate 10000, and 10000 is to get the number from a 0.xxxx to an xxxx.0000.

Also, this means it will work with *4* digit numbers too. Specifically, either 1 or 2 of them per D value, or 8 or 16 of them total.

blue_knight
01-11-2007, 02:18 AM
First you must decide how many bits to assign each value. For the following discussion we'll assume 32bit unsigned integers, where each value has 8 bits ( can be 0-255 ):

ExtractValues(uint packed_var, uint& a, uint& b, uint& c, uint& d)
{
a = (packed_var>>24)&0xff;
b = (packed_var>>16)&0xff;
c = (packed_var>>8 )&0xff;
d = packed_var&0xff;
}

The mask is 0xff = 255, which is 2^num_bits - 1, and the right shift 0,8,16,24 correspond to different parts of the 32 bit value. So (in hex again) 0xfa012030 would come out as 0xfa, 0x01, 0x20, 0x30.

Now assume 4 bits per value (0-15), its very similar except we can store 8 values:

ExtractValues(uint packed_var, uint& a, uint& b, uint& c, uint& d, uint& e, uint& f, uint& g, uint& h)
{
a = (packed_var>>28)&0x0f;
b = (packed_var>>24)&0x0f;
c = (packed_var>>20)&0x0f;
d = (packed_var>>16)&0x0f;
e = (packed_var>>12)&0x0f;
f = (packed_var>>8)&0x0f;
g = (packed_var>>4)&0x0f;
h = packed_var&0x0f;
}
So the same number above (0xfa012030) would yeild 8 values: 0xf, 0xa, 0x0, 0x1, 0x2, 0x0, 0x3, 0x0.

Anyway this is how I'd do it with plain C or C++, I haven't tried it in ZQuest yet so adjust appropriately. Are there unsigned int? Are int's 32 bit? These questions will affect how many values you can pack. And remember you can also use 1 bit flags (on/off) and get 32 flags for 32bits.

By the way arrays would be great here (hint, hint).

itsyfox
01-11-2007, 02:33 AM
Well, your version looks much more professional and like the real thing;

I just figured out what I wrote.

BUT - I don't think ZScript has either unsigned ints OR hexidecimal; and the integers it does have are just floats/fixed-point numbers that can't exceed four digits in either direction (xxxx.xxxx).

So, you could get up to 255 using the flag method, but it DOES have bitwise operators that work on regular integers - for instance, n&224, etc.

So we could probably use your method, we just have to convert the hex to decimal ourselves. ^_^

blue_knight
01-11-2007, 02:51 AM
I'm sure the ZScript ints can go higher then 255, I've used numbers up to 768 with no problems with my smooth scrolling script. However, I seem to remember reading that the numbers where 23 or 24 bit or something, the mantissa of a 32 bit float maybe? Anyway that would allow 5 or 6 4 bit values, not too bad. As for hex, if ZScript doesn't support it you'll have to convert it, but its just so nice for packing numbers. For example, given 4 bit values, 0x1234 is 1,2,3,4 but in decimal that is 4660 - not nearly as easy to read the packed values :)

jman2050
01-11-2007, 12:39 PM
Every single register value in ZScript is a 32-bit integer. No excptions. We merely simulate the idea of decimals by dividing and multiplying by 10000 where applicable. Even though it's harder to keep track of, it removes the precision problem that we had when we used floats for this purpose the first time around.