Python 中的补码

发布于 2024-08-08 17:39:12 字数 143 浏览 7 评论 0 原文

python 中是否有内置函数可以将二进制字符串(例如“111111111111”)转换为 二进制补码整数 -1?

Is there a built in function in python which will convert a binary string, for example '111111111111', to the two's complement integer -1?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(20

变身佩奇 2024-08-15 17:39:12

如果最高位为 1,则二进制补码减去 (1<。以 8 位为例,这给出了 127 到 -128 的范围。

一个对 int 进行补码的函数...

def twos_comp(val, bits):
    """compute the 2's complement of int value val"""
    if (val & (1 << (bits - 1))) != 0: # if sign bit is set e.g., 8bit: 128-255
        val = val - (1 << bits)        # compute negative value
    return val                         # return positive value as is

从二进制字符串获取特别简单...

binary_string = '1111' # or whatever... no '0b' prefix
out = twos_comp(int(binary_string,2), len(binary_string))

对我来说更有用的是从十六进制值(本例中为 32 位)...

hex_string = '0xFFFFFFFF' # or whatever... '0x' prefix doesn't matter
out = twos_comp(int(hex_string,16), 32)

Two's complement subtracts off (1<<bits) if the highest bit is 1. Taking 8 bits for example, this gives a range of 127 to -128.

A function for two's complement of an int...

def twos_comp(val, bits):
    """compute the 2's complement of int value val"""
    if (val & (1 << (bits - 1))) != 0: # if sign bit is set e.g., 8bit: 128-255
        val = val - (1 << bits)        # compute negative value
    return val                         # return positive value as is

Going from a binary string is particularly easy...

binary_string = '1111' # or whatever... no '0b' prefix
out = twos_comp(int(binary_string,2), len(binary_string))

A bit more useful to me is going from hex values (32 bits in this example)...

hex_string = '0xFFFFFFFF' # or whatever... '0x' prefix doesn't matter
out = twos_comp(int(hex_string,16), 32)
若无相欠,怎会相见 2024-08-15 17:39:12

从Python 3.2开始,有用于字节操作的内置函数: https:// /docs.python.org/3/library/stdtypes.html#int.to_bytes

通过组合 to_bytes 和 from_bytes,您可以得到

def twos(val_str, bytes):
    import sys
    val = int(val_str, 2)
    b = val.to_bytes(bytes, byteorder=sys.byteorder, signed=False)                                                          
    return int.from_bytes(b, byteorder=sys.byteorder, signed=True)

Check:

twos('11111111', 1)  # gives -1
twos('01111111', 1)  # gives 127

For old versions of Python, travc's answer is good,但如果想使用整数而不是字符串,则它不适用于负值。对于每个 val,f(f(val)) == val 为 true 的二进制补码函数是:

def twos_complement(val, nbits):
    """Compute the 2's complement of int value val"""
    if val < 0:
        val = (1 << nbits) + val
    else:
        if (val & (1 << (nbits - 1))) != 0:
            # If sign bit is set.
            # compute negative value.
            val = val - (1 << nbits)
    return val

Since Python 3.2, there are built-in functions for byte manipulation: https://docs.python.org/3/library/stdtypes.html#int.to_bytes.

By combining to_bytes and from_bytes, you get

def twos(val_str, bytes):
    import sys
    val = int(val_str, 2)
    b = val.to_bytes(bytes, byteorder=sys.byteorder, signed=False)                                                          
    return int.from_bytes(b, byteorder=sys.byteorder, signed=True)

Check:

twos('11111111', 1)  # gives -1
twos('01111111', 1)  # gives 127

For older versions of Python, travc's answer is good but it does not work for negative values if one would like to work with integers instead of strings. A twos' complement function for which f(f(val)) == val is true for each val is:

def twos_complement(val, nbits):
    """Compute the 2's complement of int value val"""
    if val < 0:
        val = (1 << nbits) + val
    else:
        if (val & (1 << (nbits - 1))) != 0:
            # If sign bit is set.
            # compute negative value.
            val = val - (1 << nbits)
    return val
狼性发作 2024-08-15 17:39:12

它不是内置的,但如果您想要不寻常的长度数字,那么您可以使用 bitstring 模块。

>>> from bitstring import Bits
>>> a = Bits(bin='111111111111')
>>> a.int
-1

同样的对象可以通过多种方式创建,包括

>>> b = Bits(int=-1, length=12)

它只是表现得像一串任意长度的位,并使用属性来获得不同的解释:

>>> print a.int, a.uint, a.bin, a.hex, a.oct
-1 4095 111111111111 fff 7777

It's not built in, but if you want unusual length numbers then you could use the bitstring module.

>>> from bitstring import Bits
>>> a = Bits(bin='111111111111')
>>> a.int
-1

The same object can equivalently be created in several ways, including

>>> b = Bits(int=-1, length=12)

It just behaves like a string of bits of arbitrary length, and uses properties to get different interpretations:

>>> print a.int, a.uint, a.bin, a.hex, a.oct
-1 4095 111111111111 fff 7777
烟凡古楼 2024-08-15 17:39:12
>>> bits_in_word=12
>>> int('111111111111',2)-(1<<bits_in_word)
-1

这是有效的,因为:

二进制的补码
number 被定义为值
减去数字得到
从二的大幂
(具体来说,对于 N 位,从 2^N
二进制补码)。两人的
数字的补码则表现为
就像原来的负片一样
大多数算术中的数字,它可以
与正数共存
自然的方式。

>>> bits_in_word=12
>>> int('111111111111',2)-(1<<bits_in_word)
-1

This works because:

The two's complement of a binary
number is defined as the value
obtained by subtracting the number
from a large power of two
(specifically, from 2^N for an N-bit
two's complement). The two's
complement of the number then behaves
like the negative of the original
number in most arithmetic, and it can
coexist with positive numbers in a
natural way.

旧城空念 2024-08-15 17:39:12

这将使用按位逻辑有效地为您提供二进制补码:

def twos_complement(value, bitWidth):
    if value >= 2**bitWidth:
        # This catches when someone tries to give a value that is out of range
        raise ValueError("Value: {} out of range of {}-bit value.".format(value, bitWidth))
    return value - int((value << 1) & 2**bitWidth)

工作原理:

首先,我们确保用户向我们传递的值在所提供的位范围内(例如,有人给我们 0xFFFF 并指定 8 位)该问题的另一个解决方案是将值与 (2**bitWidth)-1 按位与 (&)

要获得结果,该值将向左移动 1 位。这会将值的 MSB(符号位)移动到要与 2**bitWidth 进行“与”的位置。当符号位为“0”时,减数变为 0,结果为值 - 0。当符号位为“1”时,减数变为 2**bitWidth,结果为 value - 2**bitWidth

示例 1:如果参数 value=0xFF ( 255d, b11111111) 和 bitWidth=8

  1. 0xFF - int((0xFF << 1) & 2**8)
  2. 0xFF - int((0x1FE) & 0x100)
  3. 0xFF - int(0x100)
  4. 255 - 256
  5. -1

示例2:如果参数 value=0x1F (31d, b11111) 且 bitWidth=6

  1. 0x1F - int((0x1F << 1) & 2**6)
  2. 0x1F - int((0x3E) & 0x40)
  3. 0x1F - int(0x00)
  4. 31 - 0
  5. 31

示例 3:value = 0x80,bitWidth = 7

ValueError: Value: 128 out of range of 7-bit value.

示例 4:value = 0x80,bitWitdh = 8

  1. 0x80 - int((0x80 << 1) & 2**8)
  2. 0x80 - int((0x100) & 0x100)
  3. 0x80 - int(0x100)
  4. 128 - 256
  5. -128

现在,使用其他人已经发布的内容,通过将您的位字符串转换为 int(bitstring, 2) 并传递给 twos_complement 方法的 value 参数。

This will give you the two's complement efficiently using bitwise logic:

def twos_complement(value, bitWidth):
    if value >= 2**bitWidth:
        # This catches when someone tries to give a value that is out of range
        raise ValueError("Value: {} out of range of {}-bit value.".format(value, bitWidth))
    return value - int((value << 1) & 2**bitWidth)

How it works:

First, we make sure that the user has passed us a value that is within the range of the supplied bit range (e.g. someone gives us 0xFFFF and specifies 8 bits) Another solution to that problem would be to bitwise AND (&) the value with (2**bitWidth)-1

To get the result, the value is shifted by 1 bit to the left. This moves the MSB of the value (the sign bit) into position to be anded with 2**bitWidth. When the sign bit is '0' the subtrahend becomes 0 and the result is value - 0. When the sign bit is '1' the subtrahend becomes 2**bitWidth and the result is value - 2**bitWidth

Example 1: If the parameters are value=0xFF (255d, b11111111) and bitWidth=8

  1. 0xFF - int((0xFF << 1) & 2**8)
  2. 0xFF - int((0x1FE) & 0x100)
  3. 0xFF - int(0x100)
  4. 255 - 256
  5. -1

Example 2: If the parameters are value=0x1F (31d, b11111) and bitWidth=6

  1. 0x1F - int((0x1F << 1) & 2**6)
  2. 0x1F - int((0x3E) & 0x40)
  3. 0x1F - int(0x00)
  4. 31 - 0
  5. 31

Example 3: value = 0x80, bitWidth = 7

ValueError: Value: 128 out of range of 7-bit value.

Example 4: value = 0x80, bitWitdh = 8

  1. 0x80 - int((0x80 << 1) & 2**8)
  2. 0x80 - int((0x100) & 0x100)
  3. 0x80 - int(0x100)
  4. 128 - 256
  5. -128

Now, using what others have already posted, pass your bitstring into int(bitstring, 2) and pass to the twos_complement method's value parameter.

枉心 2024-08-15 17:39:12

几个实现(仅作为说明,不用于使用):

def to_int(bin):
    x = int(bin, 2)
    if bin[0] == '1': # "sign bit", big-endian
       x -= 2**len(bin)
    return x

def to_int(bin): # from definition
    n = 0
    for i, b in enumerate(reversed(bin)):
        if b == '1':
           if i != (len(bin)-1):
              n += 2**i
           else: # MSB
              n -= 2**i 
    return n

A couple of implementations (just an illustration, not intended for use):

def to_int(bin):
    x = int(bin, 2)
    if bin[0] == '1': # "sign bit", big-endian
       x -= 2**len(bin)
    return x

def to_int(bin): # from definition
    n = 0
    for i, b in enumerate(reversed(bin)):
        if b == '1':
           if i != (len(bin)-1):
              n += 2**i
           else: # MSB
              n -= 2**i 
    return n
若沐 2024-08-15 17:39:12

您可以使用 bit_length() 函数将数字转换为其二进制补码:

def twos_complement(j):
   return j-(1<<(j.bit_length()))

In [1]: twos_complement(0b111111111111)                                                                                                                                                             
Out[1]: -1

You can use the bit_length() function to convert numbers to their two's complement:

def twos_complement(j):
   return j-(1<<(j.bit_length()))

In [1]: twos_complement(0b111111111111)                                                                                                                                                             
Out[1]: -1
绾颜 2024-08-15 17:39:12

如果有人也需要相反的方向:

def num_to_bin(num, wordsize):
    if num < 0:
        num = 2**wordsize+num
    base = bin(num)[2:]
    padding_size = wordsize - len(base)
    return '0' * padding_size + base

for i in range(7, -9, -1):
    print num_to_bin(i, 4)

应该输出这个:
0111
0110
0101
0100
0011
0010
0001
0000
1111
1110
1101
1100
1011
1010
1001
1000

in case someone needs the inverse direction too:

def num_to_bin(num, wordsize):
    if num < 0:
        num = 2**wordsize+num
    base = bin(num)[2:]
    padding_size = wordsize - len(base)
    return '0' * padding_size + base

for i in range(7, -9, -1):
    print num_to_bin(i, 4)

should output this:
0111
0110
0101
0100
0011
0010
0001
0000
1111
1110
1101
1100
1011
1010
1001
1000

你爱我像她 2024-08-15 17:39:12

您可以将整数转换为字节,然后使用 struct.unpack 转换:

from struct import unpack

x = unpack("b", 0b11111111.to_bytes(length=1, byteorder="little"))
print(x)  # (-1,)

you could convert the integer to bytes and then use struct.unpack to convert:

from struct import unpack

x = unpack("b", 0b11111111.to_bytes(length=1, byteorder="little"))
print(x)  # (-1,)
云裳 2024-08-15 17:39:12

不,没有内置函数可以将二进制补码二进制字符串转换为十进制。

一个简单的用户定义函数可以执行此操作:

def two2dec(s):
  if s[0] == '1':
    return -1 * (int(''.join('1' if x == '0' else '0' for x in s), 2) + 1)
  else:
    return int(s, 2)

请注意,此函数不将位宽度作为参数,而是必须使用一个或多个前导零位来指定正输入值。

示例:

In [2]: two2dec('1111')
Out[2]: -1

In [3]: two2dec('111111111111')
Out[3]: -1

In [4]: two2dec('0101')
Out[4]: 5

In [5]: two2dec('10000000')
Out[5]: -128

In [6]: two2dec('11111110')
Out[6]: -2

In [7]: two2dec('01111111')
Out[7]: 127

No, there is no builtin function that converts two's complement binary strings into decimals.

A simple user defined function that does this:

def two2dec(s):
  if s[0] == '1':
    return -1 * (int(''.join('1' if x == '0' else '0' for x in s), 2) + 1)
  else:
    return int(s, 2)

Note that this function doesn't take the bit width as parameter, instead positive input values have to be specified with one or more leading zero bits.

Examples:

In [2]: two2dec('1111')
Out[2]: -1

In [3]: two2dec('111111111111')
Out[3]: -1

In [4]: two2dec('0101')
Out[4]: 5

In [5]: two2dec('10000000')
Out[5]: -128

In [6]: two2dec('11111110')
Out[6]: -2

In [7]: two2dec('01111111')
Out[7]: 127
倚栏听风 2024-08-15 17:39:12

由于 erikb85 提出了性能问题,这里是 travc 的回答针对 Scott Griffiths'

In [534]: a = [0b111111111111, 0b100000000000, 0b1, 0] * 1000
In [535]: %timeit [twos_comp(x, 12) for x in a]
100 loops, best of 3: 8.8 ms per loop
In [536]: %timeit [bitstring.Bits(uint=x, length=12).int for x in a]
10 loops, best of 3: 55.9 ms per loop

所以,bitstring 是,如 另一个问题,几乎比 int 慢一个数量级。但另一方面,它也很简单——我将 uint 转换为位字符串,然后转换为 int;你必须努力才能理解这一点,或者找到任何引入错误的地方。正如 Scott Griffiths 的回答所暗示的那样,该类有更多的灵活性,这可能对同一个应用程序有用。但第三方面,travc 的答案清楚地表明了实际发生的情况 - 即使是新手也应该​​能够仅通过阅读 2 行代码就能够理解从无符号 int 到 2s 补码有符号 int 的转换意味着什么。

不管怎样,与另一个关于直接操作位的问题不同,这个问题是关于对固定长度的整数(只是奇怪大小的整数)进行算术运算。所以我猜如果你需要性能,可能是因为你有一大堆这样的东西,所以你可能希望它被矢量化。将 travc 的答案调整为 numpy:

def twos_comp_np(vals, bits):
    """compute the 2's compliment of array of int values vals"""
    vals[vals & (1<<(bits-1)) != 0] -= (1<<bits)
    return vals

现在:

In [543]: a = np.array(a)
In [544]: %timeit twos_comp_np(a.copy(), 12)
10000 loops, best of 3: 63.5 µs per loop

您可能可以使用自定义 C 代码来击败它,但您可能不必这样做。

Since erikb85 brought up performance, here's travc's answer against Scott Griffiths':

In [534]: a = [0b111111111111, 0b100000000000, 0b1, 0] * 1000
In [535]: %timeit [twos_comp(x, 12) for x in a]
100 loops, best of 3: 8.8 ms per loop
In [536]: %timeit [bitstring.Bits(uint=x, length=12).int for x in a]
10 loops, best of 3: 55.9 ms per loop

So, bitstring is, as found in the other question, almost an order of magnitude slower than int. But on the other hand, it's hard to beat the simplicity—I'm converting a uint to a bit-string then to an int; you'd have to work hard not to understand this, or to find anywhere to introduce a bug. And as Scott Griffiths' answer implies, there's a lot more flexibility to the class which might be useful to the same app. But on the third hand, travc's answer makes it clear what's actually happening—even a novice should be able to understand what conversion from an unsigned int to a 2s complement signed int means just from reading 2 lines of code.

Anyway, unlike the other question, which was about directly manipulating bits, this one is all about doing arithmetic on fixed-length ints, just oddly-sized ones. So I'm guessing if you need performance, it's probably because you have a whole bunch of these things, so you probably want it to be vectorized. Adapting travc's answer to numpy:

def twos_comp_np(vals, bits):
    """compute the 2's compliment of array of int values vals"""
    vals[vals & (1<<(bits-1)) != 0] -= (1<<bits)
    return vals

Now:

In [543]: a = np.array(a)
In [544]: %timeit twos_comp_np(a.copy(), 12)
10000 loops, best of 3: 63.5 µs per loop

You could probably beat that with custom C code, but you probably don't have to.

只是一片海 2024-08-15 17:39:12

不幸的是,没有内置函数可以将无符号整数转换为二进制补码有符号值,但我们可以使用按位运算定义一个函数来执行此操作:

def s12(value):
    return -(value & 0b100000000000) | (value & 0b011111111111)

第一个按位与运算用于对负数进行符号扩展(最重要的是位被设置),而第二个用于获取剩余的 11 位。这是可行的,因为 Python 中的整数被视为任意精度的二进制补码值。

然后,您可以将其与 int 函数结合使用,将二进制数字字符串转换为无符号整数形式,然后将其解释为 12 位有符号值。

>>> s12(int('111111111111', 2))
-1
>>> s12(int('011111111111', 2))
2047
>>> s12(int('100000000000', 2))
-2048

该函数的一个很好的特性是它是幂等的,因此已经签名的值不会改变。

>>> s12(-1)
-1

Unfortunately there is no built-in function to cast an unsigned integer to a two's complement signed value, but we can define a function to do so using bitwise operations:

def s12(value):
    return -(value & 0b100000000000) | (value & 0b011111111111)

The first bitwise-and operation is used to sign-extend negative numbers (most significant bit is set), while the second is used to grab the remaining 11 bits. This works since integers in Python are treated as arbitrary precision two's complement values.

You can then combine this with the int function to convert a string of binary digits into the unsigned integer form, then interpret it as a 12-bit signed value.

>>> s12(int('111111111111', 2))
-1
>>> s12(int('011111111111', 2))
2047
>>> s12(int('100000000000', 2))
-2048

One nice property of this function is that it's idempotent, thus the value of an already signed value will not change.

>>> s12(-1)
-1
梦里梦着梦中梦 2024-08-15 17:39:12

这是将十六进制字符串中的每个值转换为其二进制补码版本的版本。


In [5159]: twoscomplement('f0079debdd9abe0fdb8adca9dbc89a807b707f')                                                                                                 
Out[5159]: '10097325337652013586346735487680959091'


def twoscomplement(hm): 
   twoscomplement='' 
   for x in range(0,len(hm)): 
       value = int(hm[x],16) 
       if value % 2 == 1: 
         twoscomplement+=hex(value ^ 14)[2:] 
       else: 
         twoscomplement+=hex(((value-1)^15)&0xf)[2:] 
   return twoscomplement            

Here's a version to convert each value in a hex string to it's two's complement version.


In [5159]: twoscomplement('f0079debdd9abe0fdb8adca9dbc89a807b707f')                                                                                                 
Out[5159]: '10097325337652013586346735487680959091'


def twoscomplement(hm): 
   twoscomplement='' 
   for x in range(0,len(hm)): 
       value = int(hm[x],16) 
       if value % 2 == 1: 
         twoscomplement+=hex(value ^ 14)[2:] 
       else: 
         twoscomplement+=hex(((value-1)^15)&0xf)[2:] 
   return twoscomplement            
唯憾梦倾城 2024-08-15 17:39:12

仍然是一个非常相关的问题,但没有一个答案在我的场景中起作用 - 这是令人惊讶的。

这是一个非常简单的函数,用于从整数值计算 n 位 2 的补码整数值。

该函数特别确保返回值不会被 python 视为负值,因为它破坏了 2 的补数的本质。

2 的补数最初是为了在本身不支持它们的架构上处理正值和负值而创建的。这是一种转换,就是使用可用的位来表示和计算正数和负数。

因此可以指定位数,默认为16,并且可以通过将其设置为0,将其设置为给定值所需的位数。

    def twos_comp(val, bits=16):
        """compute the 2's complement of int value """
        if bits == 0:      # Use as many bits needed for the value.
            bits = val.bit_length()
        return ((val & (2 ** bits) - 1) - (2 ** bits)) * -1

测试代码:

    value = 6752
    print(f'{value:05d} = 0x{value:04x} = 0b{value:016b}')
    
    value = twos_comp(value)
    print(f'{value:05d} = 0x{value:04x} = 0b{value:016b}')
    
    value = twos_comp(value)
    print(f'{value:05d} = 0x{value:04x} = 0b{value:016b}')

测试代码输出:

 06752 = 0x1a60 = 0b0001101001100000
 01440 = 0x05a0 = 0b0000010110100000
 06752 = 0x1a60 = 0b0001101001100000

Still a very relevant question, but none of the answers worked in my scenario - which is surprising.

Here's a very simple function to calculate n-bit 2's complement integer values from an integer value.

This function especially ensures that the returned value is NOT seen as a negative value by python, as it breaks the very nature of 2's complement numbers.

2's complement numbers were originally created to work with both positive and negative values on architectures that do not natively support them. It's a conversion and is all about using the bits available to represent and calculate with both positive and negative numbers.

Therefore the number of bits can be specified, defaults to 16, and can by setting it to 0, be set to the required number of bits for the given value.

    def twos_comp(val, bits=16):
        """compute the 2's complement of int value """
        if bits == 0:      # Use as many bits needed for the value.
            bits = val.bit_length()
        return ((val & (2 ** bits) - 1) - (2 ** bits)) * -1

Test code:

    value = 6752
    print(f'{value:05d} = 0x{value:04x} = 0b{value:016b}')
    
    value = twos_comp(value)
    print(f'{value:05d} = 0x{value:04x} = 0b{value:016b}')
    
    value = twos_comp(value)
    print(f'{value:05d} = 0x{value:04x} = 0b{value:016b}')

Test code output:

 06752 = 0x1a60 = 0b0001101001100000
 01440 = 0x05a0 = 0b0000010110100000
 06752 = 0x1a60 = 0b0001101001100000
橘亓 2024-08-15 17:39:12

使用~^和掩码(掩码决定总位数)

# Given negative value, obtain its two's complement form in 16 bits

>>> mask = (1 << 16) - 1
>>> a = -6
>>> bin(~(a ^ mask))
'0b1111111111111010'
# Given 16-bit signed binary string, return the integer value
>>> mask = (1 << 16) - 1
>>> b = '1111111111110101'
>>> ~(int(b, 2) ^ mask)
-11

Use ~, ^, and a mask (mask determines the total number of bits)

# Given negative value, obtain its two's complement form in 16 bits

>>> mask = (1 << 16) - 1
>>> a = -6
>>> bin(~(a ^ mask))
'0b1111111111111010'
# Given 16-bit signed binary string, return the integer value
>>> mask = (1 << 16) - 1
>>> b = '1111111111110101'
>>> ~(int(b, 2) ^ mask)
-11
梦幻的味道 2024-08-15 17:39:12

从负值中减去 2^nbits:

In [75]: (lambda x: int(x,2) - 2**len(x)*int(x[0]))('10000000')
Out[75]: -128

In [76]: (lambda x: int(x,2) - 2**len(x)*int(x[0]))('01111111')
Out[76]: 127

In [77]: (lambda x: int(x,2) - 2**len(x)*int(x[0]))('0111')
Out[77]: 7

In [78]: (lambda x: int(x,2) - 2**len(x)*int(x[0]))('1111001')
Out[78]: -7
  • 适用于任何长度输入
  • 正确处理正值和负值
  • 注意:正值必须有前导零

Subtract 2^nbits from negative values:

In [75]: (lambda x: int(x,2) - 2**len(x)*int(x[0]))('10000000')
Out[75]: -128

In [76]: (lambda x: int(x,2) - 2**len(x)*int(x[0]))('01111111')
Out[76]: 127

In [77]: (lambda x: int(x,2) - 2**len(x)*int(x[0]))('0111')
Out[77]: 7

In [78]: (lambda x: int(x,2) - 2**len(x)*int(x[0]))('1111001')
Out[78]: -7
  • works for any length input
  • handles positive and negative values correctly
  • NOTE: positive values must have leading zeros
浮生未歇 2024-08-15 17:39:12

我正在使用 Python 3.4.0

在 Python 3 中,我们在数据类型转换方面遇到了一些问题。

所以......在这里我将告诉那些经常使用十六进制字符串的人(像我一样)的技巧。

我将获取十六进制数据并对其进行补充:

a = b'acad0109'

compl = int(a,16)-pow(2,32)

result=hex(compl)
print(result)
print(int(result,16))
print(bin(int(result,16)))

结果 = -1397948151 或 -0x5352fef7 或 '-0b1010011010100101111111011110111'

I'm using Python 3.4.0

In Python 3 we have some problems with data types transformation.

So... here I'll tell a tip for those (like me) that works a lot with hex strings.

I'll take an hex data and make an complement it:

a = b'acad0109'

compl = int(a,16)-pow(2,32)

result=hex(compl)
print(result)
print(int(result,16))
print(bin(int(result,16)))

result = -1397948151 or -0x5352fef7 or '-0b1010011010100101111111011110111'

很酷不放纵 2024-08-15 17:39:12

这适用于 3 个字节。 实时代码在这里

def twos_compliment(byte_arr):
   a = byte_arr[0]; b = byte_arr[1]; c = byte_arr[2]
   out = ((a<<16)&0xff0000) | ((b<<8)&0xff00) | (c&0xff)
   neg = (a & (1<<7) != 0)  # first bit of a is the "signed bit." if it's a 1, then the value is negative
   if neg: out -= (1 << 24)
   print(hex(a), hex(b), hex(c), neg, out)
   return out


twos_compliment([0x00, 0x00, 0x01])
>>> 1

twos_compliment([0xff,0xff,0xff])
>>> -1

twos_compliment([0b00010010, 0b11010110, 0b10000111])
>>> 1234567

twos_compliment([0b11101101, 0b00101001, 0b01111001])
>>> -1234567

twos_compliment([0b01110100, 0b11001011, 0b10110001])
>>> 7654321

twos_compliment([0b10001011, 0b00110100, 0b01001111])
>>> -7654321

This works for 3 bytes. Live code is here

def twos_compliment(byte_arr):
   a = byte_arr[0]; b = byte_arr[1]; c = byte_arr[2]
   out = ((a<<16)&0xff0000) | ((b<<8)&0xff00) | (c&0xff)
   neg = (a & (1<<7) != 0)  # first bit of a is the "signed bit." if it's a 1, then the value is negative
   if neg: out -= (1 << 24)
   print(hex(a), hex(b), hex(c), neg, out)
   return out


twos_compliment([0x00, 0x00, 0x01])
>>> 1

twos_compliment([0xff,0xff,0xff])
>>> -1

twos_compliment([0b00010010, 0b11010110, 0b10000111])
>>> 1234567

twos_compliment([0b11101101, 0b00101001, 0b01111001])
>>> -1234567

twos_compliment([0b01110100, 0b11001011, 0b10110001])
>>> 7654321

twos_compliment([0b10001011, 0b00110100, 0b01001111])
>>> -7654321
花海 2024-08-15 17:39:12

好吧,我在使用 PCM wav 文件类型 时遇到了 uLaw 压缩算法 的问题。我发现,二进制补码有点使某些二进制数的负值 as可以在这里看到。在咨询wikipedia后,我认为这是真的。

那家伙将其解释为找到最低有效位并翻转其后的所有内容。我必须说上面所有这些解决方案对我没有多大帮助。当我尝试 0x67ff 时,它给了我一些关闭结果,而不是 -26623。现在,如果有人知道最低有效位正在扫描数据列表,但我不知道,因为 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短整数,我可以决定将其分割为 半字节(4 位)。如果您没有使用过按位运算符 之前。

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 

因此,我们正在搜索最低有效位,因此这里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 的补码 : ~ - 不需要任何其他数字

此操作翻转数字中的每一位。它与我们所追求的非常相似,但它没有留下最低有效位

0b10110  
~  
0b01001

正如我们在这里看到的,1 的补码与数字 XOR 完整设置位相同。


现在我们已经互相理解了,我们将通过将所有位恢复为补码中的最低有效位来获得二进制补码

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

我希望这对您或任何遇到同样问题并研究他们的问题以找到解决方案的新手有所帮助。请记住,我编写的这段代码是弗兰肯斯坦代码,这就是我为什么必须解释它的原因。它可以做得更漂亮,如果有人想让我的代码漂亮,请成为我的客人。

Ok i had this issue with uLaw compression algorithm with PCM wav file type. And what i've found out is that two's complement is kinda making a negative value of some binary number as can be seen here.And after consulting with wikipedia i deemed it true.

The guy explained it as finding least significant bit and flipping all after it. I must say that all these solutions above didn't help me much. When i tried on 0x67ff it gave me some off result instead of -26623. Now solutions may have worked if someone knew the least significant bit is scanning list of data but i didn't knew since data in PCM varies. So here is my answer:

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) )

Now since code is unreadable i will walk you through the idea.

## example data, for testing... in general unknown
data = 0x67ff # 26623 or 0110 0111 1111 1111 

This is just any hexadecimal value, i needed test to be sure but in general it could be anything in range of int. So not to loop over whole bunch of 65535 values short integer can have i decided to split it by nibbles ( 4 bits ). It could be done like this if you haven't used bitwise operators before.

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 

So we are searching for least significant bit so here the min(valid_nibble ) will suffice. Here we've gotten the place where first active (with setted bit) nibble is. Now we just need is to find where in desired nibble is our first setted bit.

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

Now here i need to clarify somethings since seeing ~ and ^ can confuse people who aren't used to this:

XOR: ^: 2 numbers are necesery

This operation is kinda illogical, for each 2 bits it scans if both are either 1 or 0 it will be 0, for everything else 1.

 0b10110
^0b11100
--------- 
 0b01010   

And another example:

 0b10110
^0b11111
---------
 0b01001

1's complement : ~ - doesn't need any other number

This operation flips every bit in a number. It is very similar to what we are after but it doesn't leave the least significant bit.

0b10110  
~  
0b01001

And as we can see here 1's compliment is same as number XOR full set bits.


Now that we've understood each other, we will getting two's complement by restoring all bites to least significant bit in one's complement.

data = ~data # one's complement of data 

This unfortunately flipped all bits in our number, so we just need to find a way to flip back the numbers we want. We can do that with bit_shift since it is bit position of our bit we need to keep. So when calculating number of data some number of bits can hold we can do that with 2**n and for nibble we get 16 since we are calculating 0 in values of bits.

2**4 = 16 # in binary 1 0000 

But we need the bytes after the 1 so we can use that to diminish the value by 1 and we can get.

2**4 -1 = 15 # in binary 0 1111 

So lets see the logic in concrete example:

 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

I hope this help'd you or any newbie that had this same problem and researched their a** off finding the solution. Have in mind this code i wrote is frankenstein code , that i why i had to explain it. It could be done more prettier, if anyone wants to make my code pretty please be my guest.

肩上的翅膀 2024-08-15 17:39:12

这比这一切要容易得多

对于 N 位上的 X, :
比较 = (-X) & (2**N - 1)

def twoComplement(number, nBits):
    return (-number) & (2**nBits - 1)

It's much easier than all that...

for X on N bits:
Comp = (-X) & (2**N - 1)

def twoComplement(number, nBits):
    return (-number) & (2**nBits - 1)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文