Author Topic: Picking a random bitwise value from a given number  (Read 2525 times)

Legacy_Lord Sullivan

  • Hero Member
  • *****
  • Posts: 671
  • Karma: +0/-0
Picking a random bitwise value from a given number
« Reply #15 on: November 12, 2011, 01:15:07 am »


               I've been reading this and been wondering in what scenario this is needed that cannot be achieved otherwise? why use bit math exactly?

I'm both interested and curious.
               
               

               
            

Legacy_Lightfoot8

  • Hero Member
  • *****
  • Posts: 4797
  • Karma: +0/-0
Picking a random bitwise value from a given number
« Reply #16 on: November 12, 2011, 01:53:43 am »


               

Lord Sullivan wrote...

I've been reading this and been wondering in what scenario this is needed that cannot be achieved otherwise? why use bit math exactly?

I'm both interested and curious.



Lets say we have a PW that has 20 towns in it, With a PC who is a thief.   Our thief has made a bad reputation for himself in many of the towns.  And we want to keep track of what towns he has behaved in, and the towns that well, would love to see him come back for a permanent stay.    We could set up a var for each town for Wanted/Unwanted  state  of the PC, in that town.  Or we could just pack all 25 into a single integer. Since the integer has 32 bits we can store 32 (31 if you take funkys warrning about using the 32nd) independet TRUE/FALSE values in the one varaiable.    

That way when our thief goes to sleep out in the wilds and that random encounter occures,  you only have the one integer to check to figure out what Town the bounty hunters are carrying papers from.    


i.e. 

const int TOWN1 = 0x01; 
const int TOWN2= 0x02;  
const int TOWN3 = 0x04; 
const int TOWN4 = 0x08;
....

SetThiefWanted( object oThief; int nTown); 
{
   int nWanted = GetLocalInt(oThief, "Wanted");
   nWanted = nWanted & nTown; 
   GetLocalInt(oThief, "Wanted", nWanted);
}

 

void main()
{
   // The Thief Has been spotted stealing stuff from 10 houses in town 1. 
   oThief = GetExitingObject();
  SetThiefWanted (oThief, TOWN1);  
}


  Once it is time for random bounty time.  You only have to check the one number to see if he is wanted in any of the towns.   in fact the GetRandomBit function above will return 0 if none of the bits are set, So it is simple enough to just bug out and not spawn the encounter if he is not wanted.
               
               

               
            

Legacy_Kato -

  • Hero Member
  • *****
  • Posts: 747
  • Karma: +0/-0
Picking a random bitwise value from a given number
« Reply #17 on: November 12, 2011, 01:55:58 am »


               

Lord Sullivan wrote...

I've been reading this and been wondering in what scenario this is needed that cannot be achieved otherwise? why use bit math exactly?

I'm both interested and curious.

Well the system is very useful when you need to store many values into a single one and access them separately or in any combination of them. For instance, my loot system reads a variable on dying creatures, and this variable holds all the categories(each represented by a bitwise value) over wich MySQL will randomize at loot creation, giving tremendous flexibility.

I also use the system for my random placeable spawner: The placeables(shrubs, stones, chests etc...) are randomly spawned on placed waypoints, but there are more waypoints than placeables, to avoid the latter from always appearing at the same location. So the spawner loops over the waypoints and for each one wich does not have a corresponding placeable(because they are destroyable), a part of its tag(the ID) holding a bitwise value is retrieved and added to an integer variable, the total. Then a random bit value is retrieved from the total, giving the ID of the waypoint where the placeable will be spawned(with a delay, of course). That last operation was giving me some troubles, hence the post.


Kato 
               
               

               


                     Modifié par Kato_Yang, 12 novembre 2011 - 02:54 .
                     
                  


            

Legacy_Lord Sullivan

  • Hero Member
  • *****
  • Posts: 671
  • Karma: +0/-0
Picking a random bitwise value from a given number
« Reply #18 on: November 12, 2011, 05:41:25 am »


               

Lightfoot8 wrote...

Lets say we have a PW that has 20 towns in it, With a PC who is a thief.   Our thief has made a bad reputation for himself in many of the towns.  And we want to keep track of what towns he has behaved in, and the towns that well, would love to see him come back for a permanent stay.    We could set up a var for each town for Wanted/Unwanted  state  of the PC, in that town.  Or we could just pack all 25 into a single integer. Since the integer has 32 bits we can store 32 (31 if you take funkys warrning about using the 32nd) independet TRUE/FALSE values in the one varaiable.    

That way when our thief goes to sleep out in the wilds and that random encounter occures,  you only have the one integer to check to figure out what Town the bounty hunters are carrying papers from.    


i.e. 

const int TOWN1 = 0x01; 
const int TOWN2= 0x02;  
const int TOWN3 = 0x04; 
const int TOWN4 = 0x08;
....

SetThiefWanted( object oThief; int nTown); 
{
   int nWanted = GetLocalInt(oThief, "Wanted");
   nWanted = nWanted & nTown; 
   GetLocalInt(oThief, "Wanted", nWanted);
}

 

void main()
{
   // The Thief Has been spotted stealing stuff from 10 houses in town 1. 
   oThief = GetExitingObject();
  SetThiefWanted (oThief, TOWN1);  
}


  Once it is time for random bounty time.  You only have to check the one number to see if he is wanted in any of the towns.   in fact the GetRandomBit function above will return 0 if none of the bits are set, So it is simple enough to just bug out and not spawn the encounter if he is not wanted.


So let's see if I get this somewhat according to your example...

Would that mean that if the random number is

0000 0000 <-- Town1, No bounty
0000 0001 <-- Town1, with bounty
0000 0010 <-- Town 2, No Bounty
0000 0011 <-- Town2, with bounty
etc... ?
               
               

               
            

Legacy_WhiZard

  • Hero Member
  • *****
  • Posts: 2149
  • Karma: +0/-0
Picking a random bitwise value from a given number
« Reply #19 on: November 12, 2011, 06:01:23 am »


               

Lord Sullivan wrote...
So let's see if I get this somewhat according to your example...

Would that mean that if the random number is

0000 0000 <-- Town1, No bounty
0000 0001 <-- Town1, with bounty
0000 0010 <-- Town 2, No Bounty
0000 0011 <-- Town2, with bounty
etc... ?


Nope.

Let's say the number stored is 1110 1101
That would mean that Town's #1,3,4,6,7,8 could possibly send a bounty out because they are hostile.
When a party is sent out it we randomly decide which Town they came from and this is returned as a power of 2 (e.g. 0010 0000 would mean that this party came from Town #6)
               
               

               
            

Legacy_Lord Sullivan

  • Hero Member
  • *****
  • Posts: 671
  • Karma: +0/-0
Picking a random bitwise value from a given number
« Reply #20 on: November 12, 2011, 06:13:16 am »


               

WhiZard wrote...

Lord Sullivan wrote...
So let's see if I get this somewhat according to your example...

Would that mean that if the random number is

0000 0000 <-- Town1, No bounty
0000 0001 <-- Town1, with bounty
0000 0010 <-- Town 2, No Bounty
0000 0011 <-- Town2, with bounty
etc... ?


Nope.

Let's say the number stored is 1110 1101
That would mean that Town's #1,3,4,6,7,8 could possibly send a bounty out because they are hostile.
When a party is sent out it we randomly decide which Town they came from and this is returned as a power of 2 (e.g. 0010 0000 would mean that this party came from Town #6)


Ah ok, position is the town, (0) or (1) means No Bounty or Bounty (Friendly) or (Hostile)

Yeah good idea. Never even crossed my mind.
               
               

               
            

Legacy_the.gray.fox

  • Full Member
  • ***
  • Posts: 214
  • Karma: +0/-0
Picking a random bitwise value from a given number
« Reply #21 on: November 12, 2011, 03:25:00 pm »


               I like these topics -- Food for the brain :-)

Since the OP has been properly helped already, I think he will not mind
if I go slightly off topic. This could interest him anyway.


--------------------------------------------------
Funky did good in warning against the flaws of the 32nd bit.
However, it is not completely unsafe to work with it.
But sure it is due care.

If you make certain that your integer is only treated as a
"repository" of bits, and you only Set and Query the bits _as bits_
(ie: like Lightfoot8 described in his "towns" example), then you
can rely on the 32nd bit safely.
In all other cases, it is wise to heed Funky warning and double-check
that all your bits are behaving. Better test 2 minutes now than debug
2 hours later -- Agree?

In regard to bits and operations with bits, I am aware of 3 specific
bugs. And more subtle ones may exist -- if anyone can list them
I will hear him gladly.


Bug 1) the float datatype
Sparing you the gory details of the floating point encoding / decoding
(you can read all about it if you google for IEEE 754 Single Precision Floating Point)
I tell you that in NWscript the Least Significant Bit in the float Mantissa is
"lost" in the act of encoding. And upon decoding it is always assumed to be 0,
which causes a small loss of precision in the final decoded value.
The Least Significant Bit (LSB) is bit 0, or the 1st bit, or the >> leftmost >> bit.
That is, the 1 in this pattern: 00000000 | 00000000 | 00000000 | 00000001
Said bit happens to be always 0 when you read from a float. And there is no
way to prevent the bug, other than not feeding to a float a value that makes use
of the LSB -- Which is not a practical solution.
Your best bet to dodge the problems caused by this is to not make use of
decimal values that want many digits of precision. The more digits you attempt
to retain, the heavier the precision loss sparked from that last 0 bit.
-
ODDLY enough, though, the NWscript float is correctly capable of storing and
retrieving any integer in the range -16777215 to +16777215 -- thus demonstrating
that the float bug strikes only when the Mantissa is employed to encode values
with a _decimal_ part whether it fits or not the 23 bits Mantissa storage.
-
Depending on the importance of your float values, it may be desirable to
pre-emptively convert them to an integer (multiplying by a proper power of 10)
so long it fits in the 23 bits range, and then assign them to the float variable.
I know it sounds extravagant -- well, extravagant solutions for extravagant bugs.
This will ensure that you retain (and can later retrieve) all your digits,
whatever the original value.



Bug 2) The >>> operator (part 1)
Consider the following code snippet:


int nInteger = -100;

int nZero = nInteger >>> 32;

nInteger set to -100 creates the following bit pattern:
11111111 | 11111111 | 11111111 | 10011100

Then the RightShift operator (>>>) is expected to clear all
the 32 bits by pushing them to the right, leaving in their
place only a streak of 0s, like this:
00000000 | 00000000 | 00000000 | 00000000

Here is the bug: the call to >>> 32 will produce no change.
nZero will be assigned the unmodified input pattern:
11111111 | 11111111 | 11111111 | 10011100
Which is wrong.

If you need to RightShift by 32 positions, you have to do so
in 2 moves, for an example: a >>> 31 followed by a >>> 1.
Any combo that sums up to 32 will work.
But avoid a >>> 16 followed by another >>> 16, because a
different bug can occur in that scenario (read below).


Bug 3) The >>> operator (part 2)
Consider the following code snippet:


int nInteger = 0xAAAA1111;

int nHiWord = nInteger >>> 16;

When RightShift-ing by 16 positions with the >>> operator,
and the 32nd bit is set... the >>> operator will behave just
like the >> operator.
That is, it will _drag_ the MSB and propagate it to its right.
Which is wrong.

In the above snippet, nHiWord should be assigned the value: 0x0000AAAA.
Instead it will be assigned the value: 0xFFFFAAAA, with the
phantom 0xFFFF0000 introduced by the MSB propagation.

To enforce the >>> behavior, in this specific case, there
is need of a [extra] masking operation to ensure that you
only retain the lower 16 bits.
Like this:


int nInteger = 0xAAAA1111;

int nHiWord = (nInteger >>> 16) & 0xFFFF;


Disclaimer:
(because the Strange does happen)
Should the code snippets I posted fail to reproduce the bugs,
I can provide the original (but way longer) source code that
will spark them sure-shot. Ask, no problem.


-fox
               
               

               


                     Modifié par the.gray.fox, 12 novembre 2011 - 03:35 .
                     
                  


            

Legacy_Kato -

  • Hero Member
  • *****
  • Posts: 747
  • Karma: +0/-0
Picking a random bitwise value from a given number
« Reply #22 on: November 12, 2011, 05:11:21 pm »


               Great infos, Fox, ty. I'll watch out for those bugs...


Kato
               
               

               
            

Legacy_Lightfoot8

  • Hero Member
  • *****
  • Posts: 4797
  • Karma: +0/-0
Picking a random bitwise value from a given number
« Reply #23 on: November 12, 2011, 08:12:18 pm »


               @the.gray.fox
Interesting results, I wish I had more time to look at them right now.  

Here is a summery of what I have seen so far.

First there seems to be no roll operators in nwscript, that makes some of the results you are getting even mor interesting.  

the << operator does a shl machine code operation.   I see no possabile problems with this one. 

the >>> operator does a sar (Shift Arithmetic Right.).   This is a  instruction that i have just never looked at.  I will have to look at it more later. 

the >> operator I also dont have a good grip on at the monent.   It looks like it can do either a single sar instruction or   it  does a  neg (computs the 2's comp)   the a sar then another neg.   
right not I just dont have the time to look at it. 


Just to make sure we are reading from the same book.   Are you running the game on an X86 system?  
               
               

               
            

Legacy_WhiZard

  • Hero Member
  • *****
  • Posts: 2149
  • Karma: +0/-0
Picking a random bitwise value from a given number
« Reply #24 on: November 12, 2011, 11:57:05 pm »


               I did a quick testing to try to decipher
100 >> 3 returns 12  (...1100100 goes to ...001100)
100 >>> 3 returns 12 (same as above)
-100 >> 3 returns -12 (...0011100 goes to ...110010)
-100 >>> 3 returns -13 (...0011100 goes to ...110011)
               
               

               
            

Legacy_WhiZard

  • Hero Member
  • *****
  • Posts: 2149
  • Karma: +0/-0
Picking a random bitwise value from a given number
« Reply #25 on: November 13, 2011, 12:34:16 am »


               

WhiZard wrote...

I did a quick testing to try to decipher
100 >> 3 returns 12  (...1100100 goes to ...001100)
100 >>> 3 returns 12 (same as above)
-100 >> 3 returns -12 (...0011100 goes to ...110010)
-100 >>> 3 returns -13 (...0011100 goes to ...110011)


I looked at the 0 shift and -100 >> 0 returns -100 just like -100 >>> 0 does.  This means that >> likely converts to positive then does the >>> shift and then converts to negative.  Thus the >>> is a true shift  of bits (though technically not logical or arithmetic)  while >> seems to be designed to model the arithmetic shift in a signed bit system.
               
               

               
            

Legacy_WhiZard

  • Hero Member
  • *****
  • Posts: 2149
  • Karma: +0/-0
Picking a random bitwise value from a given number
« Reply #26 on: November 13, 2011, 01:18:57 am »


               

WhiZard wrote...

WhiZard wrote...

I did a quick testing to try to decipher
100 >> 3 returns 12  (...1100100 goes to ...001100)
100 >>> 3 returns 12 (same as above)
-100 >> 3 returns -12 (...0011100 goes to ...110010)
-100 >>> 3 returns -13 (...0011100 goes to ...110011)


I looked at the 0 shift and -100 >> 0 returns -100 just like -100 >>> 0 does.  This means that >> likely converts to positive then does the >>> shift and then converts to negative.  Thus the >>> is a true shift  of bits (though technically not logical or arithmetic)  while >> seems to be designed to model the arithmetic shift in a signed bit system.


EDIT: The above analysis is correct though >>> seems to behave properly as an arithmetic shift according to the wikipedia article, below is demonstrated what happens when -10000 is shifted once per time with >> and with >>>.

nNumber >>> 1
-10000
-5000
-2500
-1250
-625
-313
-157
-79
-40
-20
-10
-5
-3
-2
-1 (stays at -1 indefinitely)

nNumber >> 1
-10000
-5000
-2500
-1250
-625
-312
-156
-78
-39
-19
-9
-4
-2
-1
0 (stays at 0 indefinitely)
               
               

               


                     Modifié par WhiZard, 13 novembre 2011 - 02:01 .
                     
                  


            

Legacy_WhiZard

  • Hero Member
  • *****
  • Posts: 2149
  • Karma: +0/-0
Picking a random bitwise value from a given number
« Reply #27 on: November 13, 2011, 02:59:17 am »


               I think I understand the latter two bugs.  Here is my take.

the.gray.fox wrote...
Bug 2) The >>> operator (part 1)
Consider the following code snippet:


int nInteger = -100;

int nZero = nInteger >>> 32;

This is a problem in C in general. Some implementations will just give an error.  For NWScript if the shift value used is (second operand)%32, so numbers 32 and greater (or -1 and less) will produce a shift based on their modulus base 32.   This prevents script errors as the first (or 32nd depending how you count) bit cannot be shifted.

When RightShift-ing by 16 positions with the >>> operator,
and the 32nd bit is set... the >>> operator will behave just
like the >> operator.
That is, it will _drag_ the MSB and propagate it to its right.
Which is wrong.

According to wikipedia the replication of the master bit is what is intended in an arithmetic shift.  A logical shift, as what Java does with  >>> is not what is used.
               
               

               


                     Modifié par WhiZard, 13 novembre 2011 - 03:05 .
                     
                  


            

Legacy_Lightfoot8

  • Hero Member
  • *****
  • Posts: 4797
  • Karma: +0/-0
Picking a random bitwise value from a given number
« Reply #28 on: November 13, 2011, 03:41:28 am »


               

WhiZard wrote...


When RightShift-ing by 16 positions with the >>> operator,
and the 32nd bit is set... the >>> operator will behave just
like the >> operator.
That is, it will _drag_ the MSB and propagate it to its right.
Which is wrong.

According to wikipedia the replication of the master bit is what is intended in an arithmetic shift.  A logical shift, as what Java does with  >>> is not what is used.


Yes, I don't know why I had a mental block on the Arithmetic shift earlyer,   But if we go by the lexicon has  for what the >>> orperator is supossed to do.  Bitwise Operators lexicon.
Then we have either an error in the lexicon or a bug in the operator.
               
               

               


                     Modifié par Lightfoot8, 13 novembre 2011 - 03:42 .
                     
                  


            

Legacy_WhiZard

  • Hero Member
  • *****
  • Posts: 2149
  • Karma: +0/-0
Picking a random bitwise value from a given number
« Reply #29 on: November 13, 2011, 03:52:23 am »


               

Lightfoot8 wrote...

WhiZard wrote...


When RightShift-ing by 16 positions with the >>> operator,
and the 32nd bit is set... the >>> operator will behave just
like the >> operator.
That is, it will _drag_ the MSB and propagate it to its right.
Which is wrong.

According to wikipedia the replication of the master bit is what is intended in an arithmetic shift.  A logical shift, as what Java does with  >>> is not what is used.


Yes, I don't know why I had a mental block on the Arithmetic shift earlyer,   But if we go by the lexicon has  for what the >>> orperator is supossed to do.  Bitwise Operators lexicon.
Then we have either an error in the lexicon or a bug in the operator.


Since I have never seen zero padding, I know there is some error in the lexicon, >> does not simply do a shift (for negative numbers) and >>> functions as an arithmetic shift.  Perhaps those writing it saw >>> and thought Java (since C doesn't use this notation), however >>> seems to act as >> is intended to in C while >> seems to act as if it was working in a signed bit system, and I assume BioWare intended for one of the two to be the arithmetic right shift.
               
               

               


                     Modifié par WhiZard, 13 novembre 2011 - 03:54 .