是否有可能强制0x11到0x00,保持0x01 - > 0x01和0x - > 10只使用按位运算?

时间:2012-02-20 13:11:44

标签: bit-manipulation

是否可以实现导致此映射的函数:

{
  (0x01, 0x01),
  (0x10, 0x10),
  (0x11, 0x00)
}

仅使用按位运算?

上下文

在Flixel框架中,有一组四个常量

FlxObject.LEFT:uint   = 0x0001;
FlxObject.RIGHT:uint  = 0x0010;
FlxObject.UP:uint     = 0x0100;
FlxObject.DOWN:uint   = 0x1000;

显然设计为使用按位运算符进行操作。我试图编写一个函数,只使用按位运算符,它将返回传入的任何方向(就这些FlxObject常量而言)。

一些示例映射:

{
    (0x0110, 0x1001),
    (0x0100, 0x1000),
    (0x1010, 0x0101),
    (0x0001, 0x0010),
    (0x1100, 0x0000)
}

问题是,当你传递像0x0011,0x1100,0x1110等类似的东西时,我的解决方案往往会崩溃,并且需要检查这种情况。 在这里测试代码(也在http://pastie.org/3420169):

#!/usr/bin/env python

from sys import stdout
from os import linesep

# Implementation without conditional
def horiz(dir):
    return(dir ^ 0x0011 ^ 0x1100) & 0x0011

def vert(dir):
    return (dir ^ 0x1100 ^ 0x0011) & 0x1100

def oppositeDirection(dir):
    return horiz(dir) | vert(dir)


# Implementation with conditional
def horizFix(dir):
    dir = horiz(dir)
    return dir if dir != 0x0011 else 0

def vertFix(dir):
    dir = vert(dir)
    return dir if dir != 0x1100 else 0

def oppositeDirectionFix(dir):
    return horizFix(dir) | vertFix(dir)

failcount = 0
testcount = 0

def test(dir, expect, func):
    global failcount, testcount
    testcount += 1
    result = func(dir)
    stdout.write('Testing: {0:04x} => {1:04x}'.format(dir, result))
    if result != expect:
        stdout.write('\t GOT {0:04x} expected {1:04x}'.format(result, expect))
        failcount += 1
    stdout.write(linesep)

test_cases =[0x0000, 0x0001, 0x0010, 0x0100, 0x1000, 0x0011, 0x0101, 0x1001, 0x0110, 0x1010, 0x1100, 0x0111, 0x1011, 0x1101, 0x1110, 0x1111] 


print 'Testing full oppositeDirection function----------------'
for case in test_cases:
    test(case, oppositeDirectionFix(case), oppositeDirection)

print '\nTesting horiz function---------------------------------'
for case in test_cases:
    test(case, horizFix(case), horiz)

print '\nTesting vert function----------------------------------'
for case in test_cases:
    test(case, vertFix(case), vert)


print '{0}Succeeded: {2}/{1}, Failed: {3}/{1}'.format(linesep, testcount, testcount - failcount, failcount)

如果运行测试,您将看到在0x0011和0x0000等情况下horiz将返回0x0011,而对于0x1100和0x000 vert将返回0x1100。太近了!

这显然是一个令人难以置信的微不足道的问题,我的游戏代码中永远不会有任何情况,其中方向值将同时左右或上下。但是,我以此为契机,磨练我的技巧。我在这里缺少一些逻辑原理能帮助我解决它还是意识到这是一个无法解决的问题?

2 个答案:

答案 0 :(得分:1)

不,你必须进行某种测试,因为你的结果取决于两个相邻的位。

您可以将XOR1111一起使用,然后测试结果是否包含11000011

但是,由于您只有16个值需要验证,为8个有效值设置切换/选择/匹配功能会不简单吗?

答案 1 :(得分:0)

我意识到这是一个古老的问题,但...... 如果允许位移,则可以通过按位操作来完成...虽然查找表或开关可能更实用。诀窍在于,由于结果中每个位的值取决于多个原始位,因此需要将输入值的移位副本合并到整个操作中。

如果您将测试代码中的horiz()vert()oppositeDirection()的定义更改为以下内容:

def horiz(dir):
    return (((dir << 4) & ~dir & 0x0010) | ((dir >> 4) & ~dir & 0x0001))

def vert(dir):
    return (((dir << 4) & ~dir & 0x1000) | ((dir >> 4) & ~dir & 0x1000))

def oppositeDirection(dir):
    return (((dir << 4) & ~dir & 0x1010) | ((dir >> 4) & ~dir & 0x0101))

然后所有测试都通过。如果我们查看horiz()(其他人类似):

第二位数字(从右边开始)由下式给出:

((dir << 4) & ~dir & 0x0010)

和第一个数字:

((dir >> 4) & ~dir & 0x0001)

进一步了解如何计算第一个数字,(dir >> 4)为我们提供原始的第二个数字,但排在第一个数字之后。 ~dir得到原始数字的倒数。我们然后和那些在一起,并用掩码和AND得到我们正在计算的数字,所以我们不会弄乱其他数字。

因此,如果我们调用原始数字A(第一个)和B(第二个),并调用新的第一个数字X,我们有:

X = ~A & B

第二个数字以相同的方式计算,向另一个方向移动。我们还可以通过适当调整掩码来组合水平位和垂直位的计算。