Author Topic: Tricky Bools!  (Read 774 times)

0 Members and 1 Guest are viewing this topic.

Offline z64555

  • 210
  • Self-proclaimed controls expert
    • Minecraft
    • Steam
We've been doing a bit of maintenance with the flags system in FSO, and something interesting came up that made me stop and think.

Specifically, this:

Code: [Select]
#define VM_PADLOCK_UP    (1 << 7)
#define VM_PADLOCK_REAR  (1 << 8)
#define VM_PADLOCK_LEFT  (1 << 9)
#define VM_PADLOCK_RIGHT (1 << 10)
#define VM_PADLOCK_ANY   (VM_PADLOCK_UP|VM_PADLOCK_REAR|VM_PADLOCK_LEFT|VM_PADLOCK_RIGHT)


int Viewer_mode;

// Version A
if (Viewer_mode & ~VM_PADLOCK_ANY) {}

// Version B
if (~Viewer_mode & VM_PADLOCK_ANY) {}

Note versions A and B, which differ only by the location of operator~, which is the bitwise NOT operator (makes all 0's into 1's and 1's into 0's). When placed in an if{} statement, C and C++ regards a non-boolean value (ints, floats, etc.) as TRUE if any bits in the value are 1's (that is, if the value is nonzero, it is TRUE). This little quirk will make things difficult for us as you'll soon see.

Code: [Select]
// Version A
LHS  RHS
00 & ~00 -> 00 & 11 -> 00 -> FALSE  // A right-hand operand of 00 makes no sense for flag checking, so we don't use it
01 & ~00 -> 01 & 11 -> 01 -> TRUE
10 & ~00 -> 10 & 11 -> 10 -> TRUE
11 & ~00 -> 11 & 11 -> 11 -> TRUE

00 & ~01 -> 00 & 10 -> 00 -> FALSE
01 & ~01 -> 01 & 10 -> 00 -> FALSE
10 & ~01 -> 10 & 10 -> 10 -> TRUE
11 & ~01 -> 11 & 10 -> 10 -> TRUE

00 & ~10 -> 00 & 01 -> 00 -> FALSE
01 & ~10 -> 01 & 01 -> 01 -> TRUE
10 & ~10 -> 10 & 01 -> 00 -> FALSE
11 & ~10 -> 11 & 01 -> 01 -> TRUE

00 & ~11 -> 00 & 00 -> 00 -> FALSE
01 & ~11 -> 01 & 00 -> 00 -> FALSE
10 & ~11 -> 10 & 00 -> 00 -> FALSE
11 & ~11 -> 11 & 00 -> 00 -> FALSE

//Version B
LHS  RHS
~00 & 00 -> 11 & 00 -> 00 -> FALSE
~01 & 00 -> 10 & 00 -> 00 -> FALSE
~10 & 00 -> 01 & 00 -> 00 -> FALSE
~11 & 00 -> 00 & 00 -> 00 -> FALSE

~00 & 01 -> 11 & 01 -> 01 -> TRUE
~01 & 01 -> 10 & 01 -> 00 -> FALSE
~10 & 01 -> 01 & 01 -> 01 -> TRUE
~11 & 01 -> 00 & 01 -> 00 -> FALSE
     
~00 & 10 -> 11 & 10 -> 10 -> TRUE
~01 & 10 -> 10 & 10 -> 10 -> TRUE
~10 & 10 -> 01 & 10 -> 00 -> FALSE
~11 & 10 -> 00 & 10 -> 00 -> FALSE
     
~00 & 11 -> 11 & 11 -> 11 -> TRUE
~01 & 11 -> 10 & 11 -> 10 -> TRUE
~10 & 11 -> 01 & 11 -> 01 -> TRUE
~11 & 11 -> 00 & 11 -> 00 -> FALSE


As you can see from the snippet, the two versions are not the same.

Version A seems to check if any of the flags that are 0's in the RHS operand are 1's in the LHS.

Version B seems to check if any flags that are 1's in the RHS operand are 0's in the LHS.

Let's toss in another bit to see if these general rules hold up:

Code: [Select]
// Version A
000 & ~001 -> 000 & 110 -> 000 -> FALSE
001 & ~001 -> 001 & 110 -> 000 -> FALSE
010 & ~001 -> 010 & 110 -> 010 -> TRUE
011 & ~001 -> 011 & 110 -> 010 -> TRUE
100 & ~001 -> 100 & 110 -> 100 -> TRUE
101 & ~001 -> 101 & 110 -> 100 -> TRUE
110 & ~001 -> 110 & 110 -> 110 -> TRUE
111 & ~001 -> 111 & 110 -> 110 -> TRUE

000 & ~010 -> 000 & 101 -> 000 -> FALSE
001 & ~010 -> 001 & 101 -> 001 -> TRUE
010 & ~010 -> 010 & 101 -> 000 -> FALSE
011 & ~010 -> 011 & 101 -> 001 -> TRUE
100 & ~010 -> 100 & 101 -> 100 -> TRUE
101 & ~010 -> 101 & 101 -> 101 -> TRUE
110 & ~010 -> 110 & 101 -> 100 -> TRUE
111 & ~010 -> 111 & 101 -> 101 -> TRUE

000 & ~011 -> 000 & 100 -> 000 -> FALSE
001 & ~011 -> 001 & 100 -> 000 -> FALSE
010 & ~011 -> 010 & 100 -> 000 -> FALSE
011 & ~011 -> 011 & 100 -> 000 -> FALSE
100 & ~011 -> 100 & 100 -> 100 -> TRUE
101 & ~011 -> 101 & 100 -> 100 -> TRUE
110 & ~011 -> 110 & 100 -> 100 -> TRUE
111 & ~011 -> 111 & 100 -> 100 -> TRUE

000 & ~111 -> 000 & 000-> 000 -> FALSE
001 & ~111 -> 001 & 000-> 000 -> FALSE
010 & ~111 -> FALSE
011 & ~111 -> FALSE
100 & ~111 -> FALSE
101 & ~111 -> FALSE
110 & ~111 -> FALSE
111 & ~111 -> FALSE

//Version B
~000 & 001 -> 111 & 001 -> 001 -> TRUE
~001 & 001 -> 110 & 001 -> 000 -> FALSE
~010 & 001 -> 101 & 001 -> 001 -> TRUE
~011 & 001 -> 100 & 001 -> 000 -> FALSE
~100 & 001 -> 011 & 001 -> 001 -> TRUE
~101 & 001 -> 010 & 001 -> 000 -> FALSE
~110 & 001 -> 001 & 001 -> 001 -> TRUE
~111 & 001 -> 000 & 001 -> 000 -> FALSE

~000 & 010 -> 111 & 010 -> 010 -> TRUE
~001 & 010 -> 110 & 010 -> 010 -> TRUE
~010 & 010 -> 101 & 010 -> 000 -> FALSE
~011 & 010 -> 100 & 010 -> 000 -> FALSE
~100 & 010 -> 011 & 010 -> 010 -> TRUE
~101 & 010 -> 010 & 010 -> 010 -> TRUE
~110 & 010 -> 001 & 010 -> 000 -> FALSE
~111 & 010 -> 000 & 010 -> 000 -> FALSE

~000 & 011 -> 111 & 011 -> 011 -> TRUE
~001 & 011 -> 110 & 011 -> 010 -> TRUE
~010 & 011 -> 101 & 011 -> 001 -> TRUE
~011 & 011 -> 100 & 011 -> 000 -> FALSE
~100 & 011 -> 011 & 011 -> 011 -> TRUE
~101 & 011 -> 010 & 011 -> 010 -> TRUE
~110 & 011 -> 001 & 011 -> 001 -> TRUE
~111 & 011 -> 000 & 011 -> 000 -> FALSE

~100 & 111 -> 111 & 111 -> 111 -> TRUE
~001 & 111 -> 110 & 111 -> 110 -> TRUE
~010 & 111 -> 101 & 111 -> 101 -> TRUE
~011 & 111 -> 100 & 111 -> 100 -> TRUE
~100 & 111 -> 011 & 111 -> 011 -> TRUE
~101 & 111 -> 010 & 111 -> 010 -> TRUE
~110 & 111 -> 001 & 111 -> 001 -> TRUE
~111 & 111 -> 000 & 111 -> 000 -> FALSE

Looks like our general rule seems to hold up. Version A makes less sense than version B, because usually when we do a check against flags, we're concerned about the ones that are set rather than the ones that are clear.

"But what if you wanted to check if all bits of the flag where set, or clear?"

Code: [Select]
//Checks if all bits set
(Viewer_mode & VM_PADLOCK_ANY) == VM_PADLOCK_ANY;

// Checks if all bits clear
(Viewer_mode & VM_PADLOCK_ANY) == 0;

« Last Edit: August 17, 2016, 04:11:22 pm by z64555 »
I'm on Facebook! sort of. Zeesixtyfour Fivefiftyfive

-=wxFRED2=-
R.I.P. Oliver
------------
EveningTea: Time to go Freeman on this cultist..
* EveningTea pulls crowbar off his shoulderstrap and charges screaming incoherently across the marsh *
------------
z64555: bro. do you even salad
------------
z64555: suprise double quaternion!

 

Offline z64555

  • 210
  • Self-proclaimed controls expert
    • Minecraft
    • Steam
As MageKing17 on IRC pointed out, it's preferred to do

Code: [Select]
!(Viewer_mode & VM_PADLOCK_ANY)

when checking if a single bit is clear. This notation also seems to check if all bits are clear.
I'm on Facebook! sort of. Zeesixtyfour Fivefiftyfive

-=wxFRED2=-
R.I.P. Oliver
------------
EveningTea: Time to go Freeman on this cultist..
* EveningTea pulls crowbar off his shoulderstrap and charges screaming incoherently across the marsh *
------------
z64555: bro. do you even salad
------------
z64555: suprise double quaternion!

 

Offline AdmiralRalwood

  • 211
  • The Cthulhu programmer himself!
    • Skype
    • Steam
    • Twitter
when checking if a single bit is clear.
what

I didn't say that
Ph'nglui mglw'nafh Codethulhu GitHub wgah'nagl fhtagn.

schrödinbug (noun) - a bug that manifests itself in running software after a programmer notices that the code should never have worked in the first place.

When you gaze long into BMPMAN, BMPMAN also gazes into you.

"I am one of the best FREDders on Earth" -General Battuta

<Aesaar> literary criticism is vladimir putin

<MageKing17> "There's probably a reason the code is the way it is" is a very dangerous line of thought. :P
<MageKing17> Because the "reason" often turns out to be "nobody noticed it was wrong".
(the very next day)
<MageKing17> this ****ing code did it to me again
<MageKing17> "That doesn't really make sense to me, but I'll assume it was being done for a reason."
<MageKing17> **** ME
<MageKing17> THE REASON IS PEOPLE ARE STUPID
<MageKing17> ESPECIALLY ME

<MageKing17> God damn, I do not understand how this is breaking.
<MageKing17> Everything points to "this should work fine", and yet it's clearly not working.
<MjnMixael> 2 hours later... "God damn, how did this ever work at all?!"
(...)
<MageKing17> so
<MageKing17> more than two hours
<MageKing17> but once again we have reached the inevitable conclusion
<MageKing17> How did this code ever work in the first place!?

<@The_E> Welcome to OpenGL, where standards compliance is optional, and error reporting inconsistent

<MageKing17> It was all working perfectly until I actually tried it on an actual mission.

<IronWorks> I am useful for FSO stuff again. This is a red-letter day!
* z64555 erases "Thursday" and rewrites it in red ink

<MageKing17> TIL the entire homing code is held up by shoestrings and duct tape, basically.

 

Offline z64555

  • 210
  • Self-proclaimed controls expert
    • Minecraft
    • Steam
Quote
4:34:08 PM - z64555: checking if a bit is clear. probably will go with !(Viewer_mode & VM_CAMERA_LOCKED)
4:34:21 PM - MageKing17: yes, that's how you should check that
4:34:36 PM - MageKing17: put a "not" in front of a check if a bit is set

Well, that's how I interpreted that. :P
I'm on Facebook! sort of. Zeesixtyfour Fivefiftyfive

-=wxFRED2=-
R.I.P. Oliver
------------
EveningTea: Time to go Freeman on this cultist..
* EveningTea pulls crowbar off his shoulderstrap and charges screaming incoherently across the marsh *
------------
z64555: bro. do you even salad
------------
z64555: suprise double quaternion!

 

Offline AdmiralRalwood

  • 211
  • The Cthulhu programmer himself!
    • Skype
    • Steam
    • Twitter
yes, but the way you wrote it implied that I'd said you should do something else for multiple bits when your question just hadn't been about multiple bits
Ph'nglui mglw'nafh Codethulhu GitHub wgah'nagl fhtagn.

schrödinbug (noun) - a bug that manifests itself in running software after a programmer notices that the code should never have worked in the first place.

When you gaze long into BMPMAN, BMPMAN also gazes into you.

"I am one of the best FREDders on Earth" -General Battuta

<Aesaar> literary criticism is vladimir putin

<MageKing17> "There's probably a reason the code is the way it is" is a very dangerous line of thought. :P
<MageKing17> Because the "reason" often turns out to be "nobody noticed it was wrong".
(the very next day)
<MageKing17> this ****ing code did it to me again
<MageKing17> "That doesn't really make sense to me, but I'll assume it was being done for a reason."
<MageKing17> **** ME
<MageKing17> THE REASON IS PEOPLE ARE STUPID
<MageKing17> ESPECIALLY ME

<MageKing17> God damn, I do not understand how this is breaking.
<MageKing17> Everything points to "this should work fine", and yet it's clearly not working.
<MjnMixael> 2 hours later... "God damn, how did this ever work at all?!"
(...)
<MageKing17> so
<MageKing17> more than two hours
<MageKing17> but once again we have reached the inevitable conclusion
<MageKing17> How did this code ever work in the first place!?

<@The_E> Welcome to OpenGL, where standards compliance is optional, and error reporting inconsistent

<MageKing17> It was all working perfectly until I actually tried it on an actual mission.

<IronWorks> I am useful for FSO stuff again. This is a red-letter day!
* z64555 erases "Thursday" and rewrites it in red ink

<MageKing17> TIL the entire homing code is held up by shoestrings and duct tape, basically.