34

If lv stores a long value, and the machine is 32 bits, the following code:

iv = int(lv & 0xffffffff)

results an iv of type long, instead of the machine's int.

How can I get the (signed) int value in this case?

9 Answers9

36
import ctypes

number = lv & 0xFFFFFFFF

signed_number = ctypes.c_long(number).value
Raony Barrios
  • 377
  • 3
  • 2
  • ctypes solutions are bad solutions by definition. If you want to code in C, you should probably just use C. – Bachsau Jun 18 '19 at 20:46
  • 10
    I don't normally take time to comment on comments, but I strongly disagree with @Bachsau here. I'm writing some (essentially) 'throw away' code right now to sample a bunch of sensor data from a device that generates 14-bit signed values. I just used `ctypes` to sign-extend the received value into a `ctypes.c_short`. We already have C++ code to do this in our product, so 'porting' this to `ctypes` was trivial. And once life-cycle testing is complete, nobody's gonna care about this script again. Why bash `ctypes`? It's *hella* useful for all sorts of systems programming tasks! – evadeflow Jun 20 '19 at 22:20
  • 2
    Not a bad solution if it works. Sometimes it's nice to be able to dive down and tweak some low-level stuff if you know what you're doing. This is far more explicit than some other equally low-level methods that also require bit-shifting. – iamyojimbo Oct 04 '19 at 10:12
  • 2
    Use `ctypes.c_int32` instead of `c_long` to ensure 32-bits on all platforms. – DurandA Mar 05 '20 at 20:46
22

You're working in a high-level scripting language; by nature, the native data types of the system you're running on aren't visible. You can't cast to a native signed int with code like this.

If you know that you want the value converted to a 32-bit signed integer--regardless of the platform--you can just do the conversion with the simple math:

iv = 0xDEADBEEF
if(iv & 0x80000000):
    iv = -0x100000000 + iv
hwiechers
  • 14,583
  • 8
  • 53
  • 62
Glenn Maynard
  • 55,829
  • 10
  • 121
  • 131
  • That is not accurate: all the calculations that fit into the machine's word are done in that level. For example, ~1 == -2. So I'm looking for a way to force the result into type int (which is not what int() does, apparently). –  Sep 04 '09 at 19:27
  • 1
    ~1 == -2 in Python on *all* systems. If that's not the case on the native system, then a Python implementation must emulate it. (See http://docs.python.org/reference/datamodel.html#the-standard-type-hierarchy.) The only time the native word size can leak into Python is when the size of int is greater than 32-bits, which means you make the transtion from int to long later. – Glenn Maynard Sep 04 '09 at 21:13
  • You can't force Python to convert a long to an int when the value doesn't fit in an int; you can't write Python code as if you're in C. If you want to convert the unsigned 32-bit int value 0xFFFFFFFF to the signed two's-complement 32-bit integer represented by the same bit string, then I've given you the code. Otherwise, I'm not sure what you're trying to do. – Glenn Maynard Sep 04 '09 at 21:14
  • This is ugly and is probably not terribly efficient (though it's probably more efficient than the `struct` way)... – SamB Jul 11 '10 at 00:25
  • 2
    SamB: For the question (as I interpreted it--the OP never did the courtesy of responding), it's as clean and straightforward as this sort of conversion gets. It's much cleaner and more obvious than struct. Of course, it should still be tucked away in a function and documented. – Glenn Maynard Jul 11 '10 at 11:47
  • 3
    This answer can be sort of one-liner-ized like so: `iv -= (iv & 0x80000000) << 1` – Grumdrig Apr 09 '13 at 06:52
  • 1
    @Grumdrig: That's unreadable. "One-liners" are usually not a good thing. – Glenn Maynard Apr 09 '13 at 14:10
22

Essentially, the problem is to sign extend from 32 bits to... an infinite number of bits, because Python has arbitrarily large integers. Normally, sign extension is done automatically by CPU instructions when casting, so it's interesting that this is harder in Python than it would be in, say, C.

By playing around, I found something similar to BreizhGatch's function, but that doesn't require a conditional statement. n & 0x80000000 extracts the 32-bit sign bit; then, the - keeps the same 32-bit representation but sign-extends it; finally, the extended sign bits are set on n.

def toSigned32(n):
    n = n & 0xffffffff
    return n | (-(n & 0x80000000))

Bit Twiddling Hacks suggests another solution that perhaps works more generally. n ^ 0x80000000 flips the 32-bit sign bit; then - 0x80000000 will sign-extend the opposite bit. Another way to think about it is that initially, negative numbers are above positive numbers (separated by 0x80000000); the ^ swaps their positions; then the - shifts negative numbers to below 0.

def toSigned32(n):
    n = n & 0xffffffff
    return (n ^ 0x80000000) - 0x80000000
gengkev
  • 1,890
  • 2
  • 20
  • 31
  • 4
    +1 for not using conditionals. It probably doesn't make a big difference in Python, but it certainly feels more satisfying. – zneak Jun 20 '17 at 23:22
10

Can I suggest this:

def getSignedNumber(number, bitLength):
    mask = (2 ** bitLength) - 1
    if number & (1 << (bitLength - 1)):
        return number | ~mask
    else:
        return number & mask

print iv, '->', getSignedNumber(iv, 32)
BreizhGatch
  • 101
  • 1
  • 2
9

You may use struct library to convert values like that. It's ugly, but works:

from struct import pack, unpack
signed = unpack('l', pack('L', lv & 0xffffffff))[0]
Tupteq
  • 2,986
  • 1
  • 21
  • 30
  • 5
    This can be made portable if you use one of the order/size/alignment chars. For example, use `'=l'` and `'=L'` as the format strings. – SamB Jul 11 '10 at 00:28
6

A quick and dirty solution (x is never greater than 32-bit in my case).

if x > 0x7fffffff:
    x = x - 4294967296
Rob Milne
  • 69
  • 1
  • 3
0

If you know how many bits are in the original value, e.g. byte or multibyte values from an I2C sensor, then you can do the standard Two's Complement conversion:

def TwosComp8(n):
    return n - 0x100 if n & 0x80 else n

def TwosComp16(n):
    return n - 0x10000 if n & 0x8000 else n

def TwosComp32(n):
    return n - 0x100000000 if n & 0x80000000 else n
tim
  • 482
  • 3
  • 12
0

In case the hexadecimal representation of the number is of 4 bytes, this would solve the problem.

def B2T_32(x):
  num=int(x,16)
  if(num & 0x80000000): # If it has the negative sign bit. (MSB=1)
    num -= 0x80000000*2
  return num
print(B2T_32(input("enter a input as a hex value\n")))
0

Simplest solution with any bit-length of number

Why is the syntax of a signed integer so difficult for the human mind to understand. Because this is the idea of machines. :-) Let's explain. If we have a bi-directional 7-bit counter with the initial state

000 0000

and we get a pulse for the back count input. Then the next number to count will be

111 1111

And the people said:

Hey, the counter we need to know that this is a negative reload. You should add a sign letting you know about this.

And the counter added:

1111 1111

And people asked,

How are we going to calculate that this is -1.

The counter replied: Find a number one greater than the reading and subtract it and you get the result.

   1111 1111
 -10000 0000
____________
(dec)     -1




def sigIntFromHex(a):  # a = 0x0xffe1
    if a & (1 << (a.bit_length()-1)):  # check if highest bit is 1 thru & with 0x1000
        return a - (1 << (a.bit_length()))  # 0xffe1 - 0x10000 
    else:
        return a

###and more elegant:###

def sigIntFromHex(a):
    return a - (1 << (a.bit_length())) if a & (1 << (a.bit_length()-1)) else a
        
b = 0xFFE1
print(sigIntFromHex(b))

I hope I helped

  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Oct 10 '22 at 15:31