简体   繁体   中英

Sneaking unsigned values from Jython, through Java, to C and back again

I'm involved in a project where we're binding a C API into Jython (through Java). We've run into issues with unsigned values (since Java doesn't support them). We can use casting between Java and C, but moving from Jython to Java is a harder task.

I wrote some 'casting' functions in Python. They convert a bit pattern representing a signed or unsigned value into the SAME BIT PATTERN representing the opposite sign.

For example:

>>> u2s(0xFFFFFFFF)
-1L

>>> hex(s2u(-1))
'0xffffffffL'

Is there a more elegant way to handle these sorts of sign conversions between Jython and Java? Has any one tried to do this before?

Here's the entire module:

__all__ = ['u2s', 's2u']

def u2s(v,width=32):
    """
    Convert a bit pattern representing an unsigned value to the
    SAME BIT PATTERN representing a signed value.

    >>> u2s(0xFFFFFFFF)
    -1L
    >>> u2s(0x7FFFFFFF)
    2147483647L
    """

    msb = int("1" + ((width - 1) * '0'), 2)
    msk = int("1" * width, 2)
    nv  = v & msk

    if 0 < (msb & nv):
        return -1 * ((nv ^ msk) + 1)
    else:
        return nv

def s2u(v,width=32):
    """
    Convert a bit pattern representing a signed value to the
    SAME BIT PATTERN representing an unsinged value.

    >>> hex(s2u(-1))
    '0xffffffffL'
    >>> hex(s2u(1))
    '0x1L'
    """

    msk = int("1" * width, 2)

    if 0 > v:
        return msk & (((-1 * v) ^ msk) + 1)
    else:
        return msk & v

if __name__ == "__main__":
    import doctest
    doctest.testmod()

I went and benchmarked my code VS the accepted answer in Jython. The accepted answer performs about 1/3 better! I only tested the version with explicitly defined widths.

Edit my supplied code with the following to run the benchmark for yourself:

def _u2s(v, width=32):
    fmt = {8: "B", 16: "H", 32: "I", 64: "Q"}[width]
    return struct.unpack(fmt.lower(), struct.pack(fmt, v))[0]

def _s2u(v, width=32):
    fmt = {8: "B", 16: "H", 32: "I", 64: "Q"}[width]
    return struct.unpack(fmt, struct.pack(fmt.lower(), v))[0]

if __name__ == "__main__":
    import doctest
    doctest.testmod()

    import time

    x = range(-1000000,1000000)
    t1 = time.clock()
    y = map(s2u, x)
    t2 = time.clock()

    print t2 - t1

    _t1 = time.clock()
    z = map(_s2u, x)
    _t2 = time.clock()

    print _t2 - _t1

The struct module is a natural fit for this

import struct

def u2s(v):
    return struct.unpack("i", struct.pack("I", v))[0]

def s2u(v):
    return struct.unpack("I", struct.pack("i", v))[0]

To support all the common widths

import struct

def u2s(v, width=32):
    fmt = {8: "B", 16: "H", 32: "I", 64: "Q"}[width]
    return struct.unpack(fmt.lower(), struct.pack(fmt, v))[0]

def s2u(v, width=32):
    fmt = {8: "B", 16: "H", 32: "I", 64: "Q"}[width]
    return struct.unpack(fmt, struct.pack(fmt.lower(), v))[0]

To support any width up to 64 bits

import struct

def u2s(v, width=32):
    return struct.unpack("q",struct.pack("Q",v<<(64-width)))[0]>>(64-width)

def s2u(v, width=32):
    return struct.unpack("Q",struct.pack("q",v<<(64-width)))[0]>>(64-width)

If you need to support widths above 64 bit

def u2s(v, width=32):
    return v if v < (1L<<(width-1)) else v - (1L<<width)

def s2u(v, width=32):
    return v if v >= 0 else v + (1L<<width)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM