How to get the signed integer value of a long in python?
Asked Answered
S

9

37

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?

Specular answered 3/9, 2009 at 20:54 Comment(0)
C
36
import ctypes

number = lv & 0xFFFFFFFF

signed_number = ctypes.c_long(number).value
Cthrine answered 19/2, 2012 at 17:13 Comment(4)
ctypes solutions are bad solutions by definition. If you want to code in C, you should probably just use C.Crews
I don't normally take time to comment on comments, but I strongly disagree with @Crews 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!Unchristian
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.Wallford
Use ctypes.c_int32 instead of c_long to ensure 32-bits on all platforms.Landowska
O
28

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
Obligatory answered 8/5, 2016 at 3:17 Comment(1)
+1 for not using conditionals. It probably doesn't make a big difference in Python, but it certainly feels more satisfying.Lodged
G
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
Gullet answered 3/9, 2009 at 21:3 Comment(7)
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).Specular
~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 docs.python.org/reference/…) 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.Gullet
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.Gullet
This is ugly and is probably not terribly efficient (though it's probably more efficient than the struct way)...Pisces
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.Gullet
This answer can be sort of one-liner-ized like so: iv -= (iv & 0x80000000) << 1Goddamned
@Grumdrig: That's unreadable. "One-liners" are usually not a good thing.Gullet
A
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)
Aldana answered 23/7, 2012 at 12:22 Comment(1)
Neat solution. Maybe a bit faster to use 1 << bitLength i.s.o. 2 ** bitLength, although the difference may be optimized away.Chairman
E
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]
Encounter answered 7/9, 2009 at 16:53 Comment(1)
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.Pisces
A
6

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

if x > 0x7fffffff:
    x = x - 4294967296
Aerostatic answered 27/6, 2016 at 22:34 Comment(0)
T
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
Tomato answered 17/5, 2022 at 16:36 Comment(0)
N
-1

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")))
Ninanincompoop answered 26/6, 2022 at 8:1 Comment(1)
This is the same as Glenn Maynard’s answer.Wasp
W
-2

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

Wycliffite answered 6/10, 2022 at 11:43 Comment(1)
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.Asthenic

© 2022 - 2024 — McMap. All rights reserved.