Question

This feels like it should be very simple, but I haven't been able to find an answer..

In a python script I am reading in data from a USB device (x and y movements of a USB mouse). it arrives in single ASCII characters. I can easily convert to unsigned integers (0-255) using ord. But, I would like it as signed integers (-128 to 127) - how can I do this?

Any help greatly appreciated! Thanks a lot.

Was it helpful?

Solution

Subtract 256 if over 127:

unsigned = ord(character)
signed = unsigned - 256 if unsigned > 127 else unsigned

Alternatively, repack the byte with the struct module:

from struct import pack, unpack
signed = unpack('B', pack('b', unsigned))[0]

or directly from the character:

signed = unpack('B', character)[0]

OTHER TIPS

from ctypes import c_int8
value = c_int8(191).value

use ctypes with your ord() value - should be -65 in this case

ex. from string data

from ctypes import c_int8
data ='BF'
value1 = int(data, 16) # or ord(data.decode('hex'))
value2 = c_int8(value1).value

value1 is 16bit integer representation of hex 'BF' and value2 is 8bit representation

I know this is an old question, but I haven't found a satisfying answer elsewhere.

You can use the array module (with the extra convenience that it converts complete buffers):

from array import array

buf = b'\x00\x01\xff\xfe'
print(array('b', buf))

# result: array('b', [0, 1, -1, -2])

Use this function to get a signed 8bit integer value

def to8bitSigned(num): 
    mask7 = 128 #Check 8th bit ~ 2^8
    mask2s = 127 # Keep first 7 bits
    if (mask7 & num == 128): #Check Sign (8th bit)
        num = -((~int(num) + 1) & mask2s) #2's complement
    return num

To convert any input bytes into signed integers:

def signed8bit_to_int(input):
    (((input >> 7) * 128) ^ input) - ((input >> 7) * 128)

Examples:
signed8bit_to_int(0xc0) = -64
signed8bit_to_int(0xbf) = -65
signed8bit_to_int(0x0f) = 15

Explanation with 0xC0 as an example:

  • 0xc0 '0b1100 0000', its last bit is a 1, meaning it is a signed byte
  • step 1: test for signed bit: (((input >> 7) * 128)
  • step 2: if it is a signed bit, invert the input bites:
    from: 100 0000 to 0111 1111 (63 of base 10)
  • step 3: convert the above by substantiating 128: 63 - 128 = -65

The question doesn't clearly state if the single character is given as str or bytes.

The following answer is especially useful, if the input is an instance of bytes: Since Python 3.2 there is a class method int.from_bytes:

int.from_bytes(b'\x81', byteorder='big', signed=True)  # -127
int.from_bytes(b'a', byteorder='big', signed=True)  # 97

For 16-bit integers, one would simply pass 2 bytes instead of 1:

int.from_bytes(b'\x81\x80', byteorder='big', signed=True)  # -32384
int.from_bytes(b'ab', byteorder='big', signed=True)  # 24930

Of course, you may need to specify byteorder='little'.

However, if the input is an ASCII character (0 - 128), I don't understand why you want a signed integer, because the result for your input will always be positive anyway (if I'm not mistaken). Python's integers use a flexible amount of bytes.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top