xor with 3 values

后端 未结 10 1513
野性不改
野性不改 2020-12-30 22:12

I need to do an xor conditional between 3 values, ie i need one of the three values to be true but not more than one and not none.

I thought i could use the xor ^ op

10条回答
  •  半阙折子戏
    2020-12-30 22:14

    It's a tricky one, for sure. Given what you want:

    a b c rslt
    0 0 0  0
    0 0 1  1
    0 1 0  1
    0 1 1  0
    1 0 0  1
    1 0 1  0
    1 1 0  0
    1 1 1  0
    

    This will do it:

    rslt = (!a & (b ^ c)) || (a & !(b | c));
    

    The first part handles the four cases where a is 0. The second part, where a is not 0.

    A simpler way to look at it is this:

    rslt = (a | b | c) & !((a & b) | (a & c) | (b & c))
    

    That is, one of the three must be true, but no two (or more) can be true.

    It seems like there should be a way to simplify further, but it's not coming to mind. Maybe I need more caffeine.

    EDIT

    I think this one is the solution I was looking for this morning:

    rslt = a ? !(b | c) : (b ^ c);
    

    Now, as to why I used | instead of ||:

    It's a combination of a style issue and an old bias against branching (old habits die hard). !(b | c) generates this IL code:

    ldarg.1
    ldarg.2
    or
    ldc.i4.0
    ceq
    stloc.0
    

    There aren't any branches in that code. If I use ||, as in !(b ||c), it generates:

      ldarg.1
      brfalse.s IL_009B
      ldarg.2
      br.s IL_009C
    IL_009B:
      ldc.i4.1
    IL_009C:
      stloc.0
    

    Which has two branches. I don't know if the JIT-produced code will mirror that, but I suspect it will. So one bit of code is 6 instructions that are always executed. The other is 6 instructions of which sometimes only 4 are executed. But branching penalties might very well eat up any gains from not executing two of the instructions.

    I realize that modern CPUs are much better at branching than the 8086 was, and there might not be any detectable difference in the runtime of these two code snippets. Even if there were, it's unlikely that it would make a significant difference in the overall runtime of the programs I typically write.

    But I tell you that it certainly used to! On the 8086, where branching was very expensive, the difference between (b | c) and (b || c) was huge.

    Finally, using |, as you noted, forces evaluation of the entire expression. My original code says, in effect, "if this expression is true or that expression is true." Using && and || turns it into a bunch of conditionals and is, in my brain, more difficult to read.

    So: an old prejudice based on most likely outdated performance considerations. But harmless.

    One has to be careful, though, not to write something like (b() | c()) unless both functions must be evaluated.

提交回复
热议问题