当前位置: 首页 > news >正文

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

郑昀@玩聚SR 20091013

一、现象

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

>>> 1000<<25
结果是:33554432000L
而在 C#C++等语言中执行同样的左移操作,结果却迥然不同:

Console.WriteLine(1000<<25);

结果是:-805306368

 

再举几个 Python 例子:

>>> 1000L<<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文件:

<html>
<head>
<script type="text/javascript">
alert(2083363589<<5);
</script>
</head>
<body>
</body>
</html>

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

 

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

问题何在?

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

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

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

我们先来了解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);

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<<y 相当于直接调用:

int(x * 2**y) 函数

 

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

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

--Douglas Leeder

 

三、为什么会这样?

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

Early Language Design and Development From ABC to Python

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

“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**311L<<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 00000001Python的整型数据的位宽是32位,所以要补这么长)整体左移2位,舍弃溢出的高位并在低位补零后得到结果00000000 00000000 00000000 00000100,正好是十进制数422的补码。实际上,将一个数左移几位,就相当于将这个数乘以2的几次幂。

类型长度

Python的整型数据的位宽是32位,8个字节。int 最大值是2147483647 (sys.maxint),而long 长度仅受内存大小限制。

C/C++ 中,int 的长度 机器字长 相同,16位的编译器上int16位,32位的编译器上int32位。

郑昀@玩聚SR 北京报道

 

相关文章:

  • 第六章 设计基于锁的并发数据结构
  • Silverlight 3.0 中的 Local Connection
  • Java 获取目录以及子目录下的sql文件
  • IE中iframe标签显示在DIV之上的问题解决方案
  • SharePoint : 使用SPQuery对象时要注意的事项
  • Linux 上安装JDK
  • JUnit-4.13使用报java.lang.NoClassDefFoundError: org/hamcrest/SelfDescribing错误
  • vi和vim的基本介绍
  • 流程理解篇-测试篇
  • 简单贪心题(看最多的电视节目)
  • TestDriven.NET 2.0单元测试
  • Spring Boot 2.x (十二):Swagger2的正确玩儿法
  • windows环境下memcache服务器使用经验
  • DIVCNT23 - Counting Divisors
  • 新的博客, 新的里程
  • [分享]iOS开发-关于在xcode中引用文件夹右边出现问号的解决办法
  • [原]深入对比数据科学工具箱:Python和R 非结构化数据的结构化
  • 「前端早读君006」移动开发必备:那些玩转H5的小技巧
  • 【刷算法】从上往下打印二叉树
  • JavaScript创建对象的四种方式
  • PHP CLI应用的调试原理
  • Transformer-XL: Unleashing the Potential of Attention Models
  • vue+element后台管理系统,从后端获取路由表,并正常渲染
  • Web Storage相关
  • 从@property说起(二)当我们写下@property (nonatomic, weak) id obj时,我们究竟写了什么...
  • 分类模型——Logistics Regression
  • 浮现式设计
  • - 概述 - 《设计模式(极简c++版)》
  • 工作踩坑系列——https访问遇到“已阻止载入混合活动内容”
  • 看图轻松理解数据结构与算法系列(基于数组的栈)
  • 前端技术周刊 2019-02-11 Serverless
  • 山寨一个 Promise
  • 删除表内多余的重复数据
  • 使用parted解决大于2T的磁盘分区
  • 硬币翻转问题,区间操作
  • 在weex里面使用chart图表
  • 【运维趟坑回忆录】vpc迁移 - 吃螃蟹之路
  • 关于Kubernetes Dashboard漏洞CVE-2018-18264的修复公告
  • 数据库巡检项
  • (1)Map集合 (2)异常机制 (3)File类 (4)I/O流
  • (4)STL算法之比较
  • (C语言)共用体union的用法举例
  • (C语言)深入理解指针2之野指针与传值与传址与assert断言
  • (ibm)Java 语言的 XPath API
  • (poj1.3.2)1791(构造法模拟)
  • (WSI分类)WSI分类文献小综述 2024
  • (附源码)springboot美食分享系统 毕业设计 612231
  • (附源码)计算机毕业设计SSM基于健身房管理系统
  • (论文阅读笔记)Network planning with deep reinforcement learning
  • (七)MySQL是如何将LRU链表的使用性能优化到极致的?
  • (十八)SpringBoot之发送QQ邮件
  • (一) springboot详细介绍
  • (转)一些感悟
  • ... 是什么 ?... 有什么用处?
  • .Net CF下精确的计时器