Python之位移操作符所带来的困惑

发布时间:2022-05-12 09:53:54 人气:32 作者:多测师

  一、现象

  Python 中执行左移操作(即将一个数的二进制位整体向左移若干位,移位后在低位补零,高位溢出部分舍弃):

  >>> 1000<<25

  结果是: 33554432000L

  而在 C# 、 C++ 等语言中执行同样的左移操作,结果却迥然不同:

  Console.WriteLine( 1000<<25 );

  结果是: -805306368

  再举多个 Python 例子:

  >>> 1000 L <<25 ( 注: L 后缀代表 Long 数据类型 )

  33554432000L

  >>> -1<<100

  -1267650600228229401496703205376L

  >>> 1<<100

  1267650600228229401496703205376L

  而 C# 中执行同样代码,结果有着巨大的差异:

  Console.WriteLine(1000L << 25);

  33554432000 ( LONG 类型的左移结果是一致的)

  Console.WriteLine(-1 << 100);

  -16

  Console.WriteLine(1 << 100);

  16

  Javascript. 的结果也是对的,把下面的代码保存为 html 文件:

  alert(2083363589<<5);

  阅读器打开这个 html 后得到对话框提示: -2051841888 ,也是正确的。

  那么, Python 的左移操作为何计算结果如此偏颇呢?

  疑问何在?

  即使是 Python 2.5 乃至最新的 Python 3.1.1 都是这个结果

  (只不过 Python3 执行 1000<<25 的结果是 33554432000 ,没有加 L 后缀),

  莫非这么多年来没人做左移操作吗?

  我们先来了解 Python 如何解释的吧:

Python之位移操作符所带来的困惑

  二、 Python Doc 对左移的解释

  参照 Python Doc 对左移操作符的说明 :

  A right shift by n bits is defined as division by pow(2,n ) . A left shift by n bits is defined as multiplication with pow(2,n ) ;

  Python之位移操作符所带来的困惑

  相关软件 相关文章 发表评论字体大小:【小 | 中 | 大】在手机上看

  2009-10-20 6:21:03 出处:https://www.hackhome.com 操作

  for plain integers there is no overflow check so in that case the operation drops bits and flips the sign if the result is not less than pow(2,31) in absolute value. Negative shift counts raise a ValueError exception.

  (译文:右移 n 位可以解释为除以 pow(2,n) ,左移 n 位可以解释为乘以 pow(2,n) ;对于普通整数是没有溢出检查的 , 因此若结果的绝对值不小于 pow(2,31) , 这个运算会 截掉相应的位 并且符号位也在移位处理之列 . )

  Python 的 x<

  int(x * 2**y) 函数 。

  还不要说 负数 的左移操作所遇到的疑问了:

  Shifting negative numbers doesn't have consistent interpretation between python and C. (译文:负数的位移操作, python 与 C 语言的解释是不一致的。)

  --Douglas Leeder

  三、为什么会这样?

  Python 创始人 Guido van Rossum ,在今年 2 月份的博文

  《 Early Language Design and Development : From ABC to Python 》

  中讲述了当初设计 Python 整数类型时犯下的严重不正确,以至于 “ 在特定情况下, integer 和 long 两种整数实现会有语义上的细微不同 ” ,并进一步导致:

  “In addition, the int type, while normally considered signed, was treated as

  an unsigned number by bitwise and shift operations and by conversions to/from octal and hexadecimal representations. Longs, on the other hand, were always considered signed. Therefore, some operations would produce a different result depending on whether an argument was represented as an int or a long .”

  (译文: int 类型通常情况下是有符号数,在位操作、位移操作 、和 8 进制 /16 进制互相转换时则当做无符号数 。而相对应的, long 类型则总是有符号数 。因此,某些操作会因为参数是由 int 还是 long 类型表达而产生不同的结果 。)

  他举例说:在 32 位运算中, 1<<31(1 左移 31 位 ) 是一个 32 位的大负数 ,而 1<<32 结果为 0 。然而 1L <<31(long 类型的 1 左移 31 位 ) 产生一个 long 类型整数,值为 2**31 , 1L <<32 的结果为 2**32 。

  最开始,他通过让运算结果超出存储范围时抛出溢出异常( OverflowError )修正这一不正确,

  但很快 有人抱怨这一点 ,于是他修正为:

  I should have made integer operations that overflow promote their result to longs . This is the way that Python works today, but it took a long time to make this transition.

  (译文:我应该让溢出的 int 整数操作结果 升级为 long 类型 。这也是今天 Python 采用的形式,可惜这个修正太晚了。)

  但不管怎样,位移操作的疑问始终没有被修正。

  甚至有人曾报告 Python 2.4 下 int 数左移操作会导致内存泄漏 :

  while True: x = 1 << 64 会导致内存泄漏;而 while True: 1L << 64 则不会。

  四、如何办?

  不知道。

  我们把左移操作放入 C++ 中,让 Python 调用。

  五、背景介绍

  左移运算 :

  就是将一个数的二进制位整体向左移若干位,移位后在低位补零,高位溢出部分舍弃。所以 1<<2 就是把整数 1 的二进制补码 00000000 00000000 00000000 00000001 ( Python 的整型数据的位宽是 32 位,所以要补这么长)整体左移 2 位,舍弃溢出的高位并在低位补零后得到结果 00000000 00000000 00000000 00000100 ,正好是十进制数 4 即 22 的补码。实际上,将一个数左移几位,就相当于将这个数乘以 2 的几次幂。

  类型长度

  Python 的整型数据的位宽是 32 位, 8 个字节。

  以上内容为大家介绍了Python之位移操作符所带来的困惑,希望对大家有所帮助,如果想要了解更多Python相关知识,请关注多测师。https://www.e70w.com/xwzx/


返回列表
在线客服
联系方式

热线电话

17727591462

上班时间

周一到周五

二维码
线