Python 中的进制转换

2021-09-15 11:31:36 浏览数 (1)

★本文是书稿中的一部分,主要介绍了 Python 中进制转换的实现方法。更多内容请参阅 www.itdiffer.com 我的个人网站 ”

3.4 进制转换

前面诸节所用到的整数、浮点数、分数,均是“十进制”的数,这符合数学和日常生产生活的多数习惯。而计算机则不然,它使用的是二进制(参阅第1章1.2节)。从数学角度看,用于实现记数方式的进位制除了十进制、二进制之外,还有八进制、十六进制、六十进制等。同一个数字,可以用不同的进位制表示。在数学和计算机原理的资料中,会找到如何用手工的方式实现各种进位制之间的转换——这些内容不在本书范畴,此处重点介绍使用 Python 内置函数实现进制转换,并由此观察一个貌似“ bug ”的现象。

3.4.1 转换函数

在 Python 内置函数中(如3.3节中的表3-3-1所示)提供了实现数值转换的函数,下面依次介绍。

1. 十进制转换为二进制

内置函数 bin() 能将十进制的整数转换为二进制,例如:

代码语言:javascript复制
>>> bin(2)
'0b10'
>>> bin(255)
'0b11111111'
>>> bin(-3)
'-0b11'

bin() 只能对十进制的整数进行转换,所返回值是用字符串(参阅第4章4.2节)表示的二进制数字(简称“二进制字符串”),如图3-4-1所示,其中 0b 是二进制字符串前缀。

图3-4-1 返回结果组成

若将十进制的浮点数转化为二进制,是否可以用 bin()?不能!官方文档中很明确地指出:Convert an integer number to a binary string prefixed with “0b”.(https://docs.python.org/3/library/functions.html#bin),还可以试试:

代码语言:javascript复制
>>> bin(0.1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'float' object cannot be interpreted as an integer

不能使用 bin() 函数将十进制浮点数转化为二进制,但可以用别的方法实现,只不过此处暂不探讨,可以作为读者的一个思考题。

2. 十进制转换为八进制

内置函数 oct() 可以将整数转化为以 0o 为前缀的八进制字符串,如:

代码语言:javascript复制
>>> oct(8)
'0o10'
>>> oct(256)
'0o400'

注意参数依然必须是整数。

3. 十进制转换为十六进制

内置函数 hex() 可以将整数转化为以 0x 为前缀的十六进制字符串,如:

代码语言:javascript复制
>>> hex(16)
'0x10'
>>> hex(255)
'0xff'

在十六进制中,一般用数字

0

9

和字母

A

F

表示。在 hex() 返回的十六进制字符串中,所用的

A

F

的字母均为小写。

对于十进制的浮点数,虽然 hexo() 不能使用,但浮点数对象有一个方法可以实现向十六进制的转换。

代码语言:javascript复制
>>> float.hex(0.1)
'0x1.999999999999ap-4'
>>> 0.1.hex()
'0x1.999999999999ap-4'

其实,这里得到的十六进制字符串与十进制浮点数 0.1 并非严格相等。

4. 二进制转换为十进制

如果在交互模式中直接输入二进制数,比如 01,Python 解释器并不接受——所接受的是十进制数。

代码语言:javascript复制
>>> 01
  File "<stdin>", line 1
    01
     ^
SyntaxError: leading zeros in decimal integer literals are not permitted; use an 0o prefix for octal integers

当然,有的读者可能输入的是 11 ,不会报错,但 Python 把它看做二进制 11 了吗?没有!它显然被 Python 认定为十进制的整数十一了。

代码语言:javascript复制
>>> 11
11
>>> type(11)
<class 'int'>

但是,如果在交互模式中这样输入二进制数字:

代码语言:javascript复制
>>> 0b11
3
>>> 0b10
2
>>> 0b11111111
255

注意,输入的不是“二进制字符串”,而是在二进制数前面写上了前缀 0b,表示当前所输入的是二进制数,返回值则是对应的十进制整数。这种方式仅限于交互模式,在程序文件中不能这样做——千万不要将 >>> 0b11 复制到 .py 文件中。

更常用的方法是使用内置函数 int() ,在3.3.1节中提到过, int(x, base=10) -> integer 会在本节介绍,就是这里:

代码语言:javascript复制
>>> int('0b10', 2)
2
>>> int('11111111', 2)
255

其中的'0b10''11111111' 都是二进制字符串,并且要设置参数 base=2 ,即说明参数中的数字是二进制数字——不明确“告诉” Python ,它不知道 '0b10''11111111' 表示的是二进制字符串。

同样用 int() 函数,也能将八进制、十六进制的整数转换为十进制的整数。

代码语言:javascript复制
>>> int('0xff', base=16)
255
>>> int('0o10', base=8)
8

3.4.2 不是 bug

在3.3.1节介绍内置函数 round() 时引用了官方文档的一个示例:

代码语言:javascript复制
>>> round(2.675, 2)
2.67

该文档中明确说明:“This is not a bug”。那是什么呢?

类似的现象,其实还有,比如:

代码语言:javascript复制
>>> 0.1   0.2
0.30000000000000004
>>> 0.1   0.1   0.1 - 0.3
5.551115123125783e-17
>>> 0.1   0.1   0.1 - 0.2
0.10000000000000003

这些计算中都有一些“误差”,也不是 Python 的 bug。

由于计算机是执行二进制计算的,要完成十进制数字的计算,不得不将十进制数字转化为二进制。对于十进制的整数而言,都有精确的二进制数对应。但是,对于浮点数就不完全有精确的二进制数对应了。

例如,

0.1

转化为二进制是:

0.0001100110011001100110011001100110011001100110011...

。这个二进制数不精确等于十进制数

0.1

。同时,计算机存储的位数是有限制的,所以,就出现了上述“误差”。

这种问题不仅在 Python 中会遇到,在所有支持浮点数运算的编程语言中都会遇到,所以它不是 Python 的 bug 。

明白了原因后,该怎么解决呢?就 Python 的浮点数运算而言,大多数计算机每次计算误差不超过

frac{1}{2^{53}}

。对于大多数任务来说,通过“四舍五入”(round() 函数,参阅3.3.1节)即能得到期望的结果。

但是,如果遇到“较真”的要求,怎么办?

代码语言:javascript复制
>>> import decimal
>>> a = decimal.Decimal('0.1')
>>> b = decimal.Decimal('0.2')
>>> a   b
Decimal('0.3')
>>> float(a   b)
0.3

decimal 是 Python 标准库的一个模块,官方地址是 https://docs.python.org/3/library/decimal.html 。如上述代码示例,分别创建了与浮点数

0.1

0.2

对应的两个对象( decimal.Decimal 类型),它们之间相加,所得结果即为准确的

0.3

。但是这样计算的速度要低于浮点数运算。

decimal 模块不仅能解决上述计算“误差”问题,还有很多用途,比如某个业务要求所有计算结果必须有

3

位有效数字(注意与“四舍五入”不同),可以这样实现:

代码语言:javascript复制
>>> from decimal import Decimal, getcontext
>>> getcontext().prec = 3
>>> Decimal(1) / Decimal(7)
Decimal('0.143')
>>> Decimal(3) * Decimal(math.pi) ** 2
Decimal('29.6')

此外,decimal.Decimal 对象亦有支持常见计算的方法。建议读者在学习完第8章和第9章,再认真阅读此模块的官方文档,并练习使用其中的各个方法。

自学建议 编程语言中是基于计算机基本原理而存在的,因此,建议读者在学习本书之余——学有余力,阅读有关计算机基本原理的书籍,这样不仅对编程语言,乃至于以后的工作实际都会大有裨益。例如对于本节提到的“进制转换带来的计算误差”问题,要想“知其所以然”,就不得不求助于计算机基本原理的知识。 我在个人网站 www.itdiffer.com 和微信公众号【老齐教室】都会发布有关计算机原理的内容,读者可查阅参考。 ”

0 人点赞