科学计数法通常用于表示极大或极小的数字。Python 使用 E 表示法 显示科学计数法,如下图所示。
图 1-1 科学计数法的显示方式图 1-1 中使用科学计数法格式化了两个数字。其中,第一种形式是科学计数法的传统形式,第二种形式是 Python 的 E 表示法。
格式说明符也可以用于显示科学计数法中的数字,f-string 通常使用小写字母 e
来实现科学计数法。E 表示法如例 14 所示。
1>>> f"{1234.56789:.2e}"
2(14.1) '1.23e+03'
3
4>>> f"{0.00012345:.3e}"
5(14.2) '1.234e-04'
例(14.1)中的精度为 .2e
,因此输出结果以科学计数法显示,并四舍五入到小数点后两位。
科学计数法也可以添加宽度参数,和正常的数字格式化一样,也使用填充符满足宽度的需求。
例(14.2)中的数字非常小,因此,科学计数法将指数乘以-04
。例 14 中使用 .3e
将结果显示到小数点后三位。
注意:格式说明符只是显示工具,为了保证计算结果的准确,所有计算结果仍使用原始数字。
格式说明符还可以格式化复数。复数广泛用于与振荡有关的方程。例如,作为傅里叶变换的一部分来消除真实传输中的噪声频率。
复数是由实部和虚部组成的,其中,虚部是 -1
的平方根的倍数。通常写成 a+bi
的形式,其中 a
是实部,b
是虚部。
注意:Python 使用
j
表示虚部,不是i
。
尽管复数有些抽象,但仍可以使用格式说明符,详见例 15。
1>>> value = 3.474 + 2.323j
2>>> print(
3... f"复数 {value} 的组成如下。",
4... f"实部为 {value.real:.2f},",
5... f"虚部为 {value.imag:.1f},",
6... f"约等于 {value:.0f}。",
7... sep="\n",
8... )
9复数 (3.474+2.323j) 的组成如下。
10实部为 3.47,
11虚部为 2.3,
12约等于 3+2j。
f-string 使用 .real
和 .imag
访问复数的实部和虚部。通过例 15 可以看出,格式化复数与格式化其他数字没有什么不同。
此外,使用格式说明符还可以格式化整个复数,即同时格式化实部和虚部。
例 15 中的 .2f
让实部保留两位小数,.1f
让虚部保留一位小数,.0f
让整个复数保留 0 位小数。
下一节将介绍 f-string 更高阶的使用方法,在继续学习之前,再做一些练习题吧。
练习 7:使用 value = 13579+0.0245j
,编写一行代码生成如下所示的输出结果。
1>>> value = 13579+0.0245j
2>>> print(
3... # 在此写下你的解决方案
4... )
5
6实部为 1.36E+04,虚部为 2.45E-02。
注意,本题使用的是大写 E
。
如果有不明白的地方,请参阅 Python 官方文档与格式说明符相关的内容。
Python 的 float 类型规定数字的存储上限是 64 位,也就是说,如果数字超过这个大小将无法准确表示。如例 16 所示。
1>>> f"{(10000000000000 / 3)}"
2'3333333333333.3335'
正确的答案应该是 3 的无限循环,但由于存储空间的限制,小数点中出现了 5。
在这种情况下,即便使用格式说明符 .2f
将输出显示到两位小数位也不能解决问题,误差仍然存在。因为之前说过,格式说明符只是设置了四舍五入的显示效果,而不是截取数字。
为了说明这个问题,我们看一下例 17。
1>>> f"{(10000000000000 / 3) + 0.6666}"
2'3333333333334.0'
例 17 显示的输出结果不准确,甚至它的整数部分都出现了错误。在此基础上继续计算将错上加错,最终结果会更不准确。
尽管浮点数运算的精度问题涉及计算机设计的底层逻辑,但为了确保计算结果的准确度,推荐使用内置的 decimal
模块。
decimal
对象提供比 float
类型更多的浮点数算术控制,能够以一致的精度执行计算,并把精度作为其计算的重心。
例 18 展示了使用 decimal
的优势。
1>>> from decimal import Decimal as D
2>>> from decimal import getcontext
3
4>>> getcontext().prec = 4
5>>> f"¥{float("0.1") + float("0.1") + float("0.1")}"
6'¥0.30000000000000004'
7
8>>> f"¥{D("0.1") + D("0.1") + D("0.1")}"
9'¥0.3'
decimal
属于 Python 的标准库,无需安装,但必须先导入 decimal
才能访问用于实例化对象的 Decimal
类。
通过大写字母 D
导入 Decimal
,可以在创建实例时使用别名,让代码更简洁。
例 18 中还导入了 getcontext()
函数。在使用 Decimal
对象时,其关键属性大多是由上下文对象集中管理的,getcontext()
函数的作用就是获取上下文。
接下来,使用 getcontext()
函数的 .prec
属性设置 Decimal
对象的精度。它定义了 Decimal
对象有效数字的位数,Decimal
实例将会四舍五入以适应其精度。
如例 18 所示,在使用 float
时,输出了错误的结果。然而,在使用 Decimal
实例时,没有出现错误。
如果对精度的要求高,则应在使用格式说明符前先将字符串转换为小数(不是浮点数),以预防计算出错,因为超出精度之外的内容已经被安全地删除了。
此外,小数的精度更高,默认精度为 28 位,而浮点数只有约 16 位。
注意:使用内置的
round()
函数四舍五入float
类型的值也可以减少误差,但使用decimal
模块支持仅在一处修改,即可对上下文对象中多个数字的精度进行修改,并且Decimal.quantize()
方法还提供了更多对Decimal
对象进行四舍五入的控制方式。
在选择精度值时,可以使用警戒数(guarding figures)。
例如,在计算货币时,即使计算结果只包含两位小数,但在计算过程中仍推荐使用 4 位小数,以减少四舍五入对最终结果的影响。
总的来说,在进行计算时,建议使用比显示的小数位多两位的警戒数。
通过精度和格式说明符的组合可以使用警戒数。格式说明符在 Decimal
对象中的使用方式与浮点数一样。具体实现方式如例 19 所示。
1>>> getcontext().prec = 4
2>>> f"¥{D("0.10001") + D("0.20001"):.2f}"
3'¥0.30'
开始时,先把两个包含微小误差的数字以原始形式相加。由于上下文中的精度被设置为 4 位有效数字,当每个字符串被转换为 Decimal
对象时,将会移除这些误差。然后,再使用格式说明符把计算结果显示为四舍五入的两位小数。
注意:尽管
decimal
模块对浮点数的支持很好,但仍无法彻底解决与浮点数相关的四舍五入问题。
Decimal
对象的显示如例 20 所示。
1>>> D(0.1)
2Decimal('0.1000000000000000055511151231257827021181583404541015625')
在全局精度为 4 时,预期的答案是 0.1000
。但这里的精度却被忽略了,这是因为 getcontext().prec
属性仅对表达式的结果起作用,不能直接用于浮点数。在此,可以强制应用精度,只是这样写的代码有些奇怪。实现方式如例 21 所示。
1>>> D(0.1) * D(1)
2Decimal('0.1000')
用 D(0.1)
乘以 D(1)
,就能应用有效数字为 4
的精度。
Python 还支持更多用字符串格式化数字的方法,下一节再继续介绍。
未完待续。
虽然小编已经尽力避免错误,但本文仍可能存在 bug,欢迎读者在公众号后台进行反馈。
相关阅读
32 个 Python 实例彻底解析 f-String 格式化浮点数 001【推荐收藏】
32 个 Python 实例彻底解析 f-String 格式化浮点数 002【推荐收藏】