V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
justou
V2EX  ›  Python

踩了个陷阱: Python bitwise ~ vs. C bitwise ~

  •  1
     
  •   justou · 2017-06-19 16:45:36 +08:00 · 1933 次点击
    这是一个创建于 2474 天前的主题,其中的信息可能已经有所发展或是发生改变。

    有如下 C 代码:

    #include <stdio.h>
    
    int main()
    {
        int x = 0x87654321;
        int mask = 0xFF;                            
    
        printf("0x%08x\n", ~mask);
        printf("0x%08x\n", x & mask);    
        printf("0x%08x\n", x ^ ~mask);   
        printf("0x%08x\n", x | mask); 
        return 0;
    }
    

    输出:

    0xffffff00
    0x00000021
    0x789abc21
    0x876543ff
    

    改成对应的 python 代码:

    x = 0x87654321
    mask = 0xFF
    print("0x%08x" % (~mask))
    print("0x%08x" % (x & mask))
    print("0x%08x" % (x ^ ~mask))
    print("0x%08x" % (x | mask))
    

    输出:

    0x-0000100
    0x00000021
    0x-876543df
    0x876543ff
    

    请问 python 输出与 C 输出不一致的原因是什么,能否修改 python 代码使其与 C 输出一致?

    6 条回复    2017-06-20 14:10:35 +08:00
    pright
        1
    pright  
       2017-06-19 17:28:25 +08:00   ❤️ 1
    import ctypes

    print("0x%08x" % (ctypes.c_uint(~mask).value))
    enenaaa
        2
    enenaaa  
       2017-06-19 17:48:12 +08:00   ❤️ 1
    好像是被当成有符号数打印了。
    试一下 &0xffffffff。这样
    print("0x%08x" % ((~mask)&0xffffffff))
    popstk
        3
    popstk  
       2017-06-19 17:54:49 +08:00   ❤️ 1
    <code>def foo(n, size=32):
    return ((1<<size)-1)&n


    x = 0x87654321
    mask = 0xFF
    print("0x%08x" % foo(~mask))
    print("0x%08x" % foo(x & mask))
    print("0x%08x" % foo(x ^ ~mask))
    print("0x%08x" % foo(x | mask))
    </code>

    https://wiki.python.org/moin/BitwiseOperators
    NoAnyLove
        4
    NoAnyLove  
       2017-06-20 00:48:15 +08:00   ❤️ 3
    因为 Python 的 int 是无限长度的,如果对一个正整数取反,会变成负数,你可以认为符号位是无限长度的 1:

    ```
    >>> mask = 0xFF
    >>> bin(mask)
    '0b11111111'
    >>> ~mask
    -256
    >>> bin(~mask)
    '-0b100000000'
    ```

    `bin(~mask)`返回结果'-0b100000000',前面的`-0`就是相当于无限长度的符号位,如果用 Two ‘ s Complement 来表示就全是 1.

    解决这个问题也很简单,将结果按位与 0xFFFFFFFF (刚好 32 位 bit )运算,就可以消除掉超过 32 位的无限长度的符号位。

    ```
    >>> ~mask & 0xFFFFFFFF
    4294967040
    >>> hex(_)
    '0xffffff00'
    ```
    justou
        5
    justou  
    OP
       2017-06-20 11:23:05 +08:00
    感谢楼上各位,4L 给了个很好的解释
    pright
        6
    pright  
       2017-06-20 14:10:35 +08:00
    这里不一致的原因其实就是 C 的 printf 的%x 对应的是 unsigned hexadecimal integer,而 python 的 print 的%x 对应的是 signed hexadecimal integer。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1000 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 19:54 · PVG 03:54 · LAX 12:54 · JFK 15:54
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.