好的,我在 PCM wav 文件类型的 uLaw 压缩算法 中遇到了这个问题。我发现二进制补码有点使某个二进制数为负值as can be seen here。在咨询wikipedia 之后,我认为这是真的。
那家伙解释为找到 least significant bit 并在它之后翻转。我必须说,上述所有这些解决方案对我没有多大帮助。当我尝试使用0x67ff 时,它给了我一些结果,而不是-26623。如果有人知道 least significant bit 正在扫描数据列表,现在解决方案可能已经奏效,但我不知道,因为 PCM 中的数据会有所不同。所以这是我的答案:
max_data = b'\xff\x67' #maximum value i've got from uLaw data chunk to test
def twos_compliment(short_byte): # 2 bytes
short_byte = signedShort(short_byte) # converting binary string to integer from struct.unpack i've just shortened it.
valid_nibble = min([ x*4 for x in range(4) if (short_byte>>(x*4))&0xf ])
bit_shift = valid_nibble + min( [ x for x in [1,2,4,8] if ( ( short_byte>>valid_nibble )&0xf )&x ] )
return (~short_byte)^( 2**bit_shift-1 )
data = 0x67ff
bit4 = '{0:04b}'.format
bit16 = lambda x: ' '.join( map( bit4, reversed([ x&0xf, (x>>4)&0xf, (x>>8)&0xf, (x>>12)&0xf ]) ) )
# print( bit16(0x67ff) , ' : ', bit16( twos_compliment( b'\xff\x67' ) ) )
# print( bit16(0x67f0) , ' : ', bit16( twos_compliment( b'\xf0\x67' ) ) )
# print( bit16(0x6700) , ' : ', bit16( twos_compliment( b'\x00\x67' ) ) )
# print( bit16(0x6000) , ' : ', bit16( twos_compliment( b'\x00\x60' ) ) )
print( data, twos_compliment(max_data) )
现在由于代码不可读,我将引导您完成这个想法。
## example data, for testing... in general unknown
data = 0x67ff # 26623 or 0110 0111 1111 1111
这只是任何十六进制值,我需要测试才能确定,但通常它可以是 int 范围内的任何值。因此,不要循环遍历整个 65535 值short integer 可以让我决定将其拆分为 nibbles(4 位)。如果您之前没有使用过bitwise operators,可以这样做。
nibble_mask = 0xf # 1111
valid_nibble = []
for x in range(4): #0,1,2,3 aka places of bit value
# for individual bits you could go 1<<x as you will see later
# x*4 is because we are shifting bit places , so 0xFA>>4 = 0xF
# so 0x67ff>>0*4 = 0x67ff
# so 0x67ff>>1*4 = 0x67f
# so 0x67ff>>2*4 = 0x67
# so 0x67ff>>3*4 = 0x6
# and nibble mask just makes it confided to 1 nibble so 0xFA&0xF=0xA
if (data>>(x*4))&nibble_mask: valid_nibble.append(x*4) # to avoid multiplying it with 4 later
所以我们正在搜索least significant bit,所以这里min(valid_nibble ) 就足够了。在这里,我们得到了第一个活动(设置位)半字节的位置。现在我们只需要找到所需的半字节中我们的第一个设置位。
bit_shift = min(valid_nibble)
for x in range(4):
# in my example above [1,2,4,8] i did this to spare python calculating
ver_data = data>>min(bit_shift ) # shifting from 0xFABA to lets say 0xFA
ver_data &= nibble_mask # from 0xFA to 0xA
if ver_data&(1<<x):
bit_shift += (1<<x)
break
现在我需要澄清一些事情,因为看到~ 和^ 会让不习惯的人感到困惑:
XOR:^: 2 个数字是必需的
这个操作有点不合逻辑,对于它扫描的每 2 位,如果两者都是 1 或 0,它将为 0,否则为 1。
0b10110
^0b11100
---------
0b01010
还有一个例子:
0b10110
^0b11111
---------
0b01001
1's complement : ~ - 不需要任何其他号码
此操作翻转数字中的每一位。它与我们所追求的非常相似,但它不会留下最低有效位。
0b10110
~
0b01001
正如我们在这里看到的,1 的恭维与数字 XOR 完整的位相同。
现在我们已经相互理解了,我们将通过将所有位恢复到一个补码中的最低有效位来获得two's complement。
data = ~data # one's complement of data
不幸的是,这翻转了我们数字中的所有位,所以我们只需要找到一种方法来翻转我们想要的数字。我们可以使用bit_shift 来做到这一点,因为它是我们需要保留的位的位位置。因此,当计算数据的数量时,我们可以使用2**n 来计算数据的数量,对于半字节,我们得到 16,因为我们计算的是 0 的位值。
2**4 = 16 # in binary 1 0000
但是我们需要1 之后的字节,所以我们可以使用它来将值减1,然后我们可以得到。
2**4 -1 = 15 # in binary 0 1111
那么让我们看看具体例子中的逻辑:
0b110110
lsb = 2 # binary 10
~0b110110
----------
0b001001 # here is that 01 we don't like
0b001001
^0b000011 # 2**2 = 4 ; 4-1 = 3 in binary 0b11
---------
0b001010
我希望这对您或遇到同样问题并研究他们的**以找到解决方案的任何新手有所帮助。请记住我编写的这段代码是科学怪人代码,这就是我为什么必须解释它的原因。它可以做得更漂亮,如果有人想让我的代码漂亮,请成为我的客人。