零. 前言
在python中, 数字是没有最大值的, 也就是说, 我们可以给python的变量赋一个很大的值并且可以进行很多的数学操作, 这个特效让我的密码学课设一天就完成了, 爽到爆炸
python作为一个对初学者友好的语言, 必然会被广泛的用到其他和数学关系密切的领域, 这些领域的专家会用python写很多很多本行业的库, 作为开发, 那么熟悉和了解Python的数字类型和其中的很多坑必然是绕不过去的坎
一. 初始化和格式化
1.1 数据初始化
python中数字的初始化很简单, 直接等于号赋值就行, 由于Python 的数字长度是没有限制的, 所以无论多大的数都是可以直接赋值, 浮点数也是同理
在Python中, 整数和浮点数是不同的数据类型
number_int = 123456789
number_float = 1.23125251
print(type(number_int)) # ouput: <class 'int'>
print(type(number_float)) # ouput: <class 'float'>
但是大整数在数有多少个0的时候非常吃力, 所以python的大整数是可以用下滑线隔开的
number_int = 123456789
number_int_with_underscores = 123_456_789
此外, 由于Python的整数前面可以带正负号, 所以可以在数字间++—乱加(ps: 工作这样写大概率要挨揍😊)
number_int = +-+-123_456_789+-123 # is ok
除了直接赋值外, python还可以用类型转换函数将字符串类型的数字转换为数字类型的数字
var_int = "123456789"
var_float = "1.23125251"
number_int = int(var_int) # 123456789
number_float = float(var_float) # 1.23125251
这里需要注意的是, python的数字的类型转换函数可以互相使用, 但整数被转换成浮点数的时候, 会加一个小数点, 后面跟0; 当 浮点数被转换成整数的时候, 会默认丢弃小数点后面的数字
float_int = 123456789
float_float = 1.23125251
number_int = float(float_int) # 123456789.0
number_float = int(float_float) # 1
1.2 数据的格式化
1.2.1 整数类型的科学记数法
科学计数法的赋值很简单:
a = 3e5 # a=300000.0
b = 3e-2 # b=0.03
但是一半很少有人会在开发中用科学计数法去赋值, 更多的是读取一些来自表格的或者其他来源的表示为科学计数法的数字, 直接对其转换类型就可以, 这里要注意的是转换成具体的数字只能用float转换为浮点数
a = "3e5"
b = float(a)
1.2.2 浮点数的小数限制
python默认的format函数可以限制浮点数的位数, 但是返回的会是字符串
g代表的是所有的个数包含前面的整数
f代表的是小数点后面的个数
print(format(1.234, '.2g')) # output: 1.2
print(format(1.234, '.2f')) # output: 1.23
python提供了一个round函数, 用于控制浮点数的小数点后个数, 默认会四舍五入
a = 3.156
res = round(3.153, 2) # res: 3.15
a = 3.154
res = round(3.155, 2) # res: 3.16
但是需要注意的是, 这里有可能会出现有问题的数据
a = 3.155
res = round(3.155, 2) # res: 3.15 ????
这个是由于python的取整函数用的是
rounding ties to even
算法, 这个算法的思想是5前面的数字, 如果是偶数, 就向下取整, 如果是奇数, 就先上取整
a = 2.15
b = 3.15
c = 4.115
d = 5.115
e = 6.1115
f = 7.1115
print(round(a, 1)) # output: 2.1
print(round(b, 1)) # output: 3.1
print(round(c, 2)) # output: 4.12
print(round(d, 2)) # output: 5.12
print(round(e, 3)) # output: 6.112
print(round(f, 3)) # output: 7.112
但是这里可以看到, 2.1和3.1都是奇数, 理论上应该进1, 但是这里却没有进1, 原因是这个数字后面还有其他的数字(未证实, 涉及到python的小数点实现, 后面会说)
二. 数字计算
在Python中, 可以非常方便的进行数字的加减乘除
a = 1 + 1
b = 2 - 1
c = 2 * 2
d = 2 / 2
e = 2 ** 2
f = 3 % 2
原理是这些数据都是数字类的实例对象, 在这些对象中各自实现了__add__函数等
所以, 如果我们自己实现这些函数, 也可以参与到python的数字运算中
其中__add__是我们自己的示例对象加其他的对象,__radd__是我们的对象被加; 其中, 前者数字的__add__函数会被优先执行
class MyIntegral(Integral):
def __add__(self, other):
if isinstance(other, MyIntegral):
return do_my_adding_stuff(self, other)
elif isinstance(other, OtherTypeIKnowAbout):
return do_my_other_adding_stuff(self, other)
else:
return NotImplemented
def __radd__(self, other):
if isinstance(other, MyIntegral):
return do_my_adding_stuff(other, self)
elif isinstance(other, OtherTypeIKnowAbout):
return do_my_other_adding_stuff(other, self)
elif isinstance(other, Integral):
return int(other) + int(self)
elif isinstance(other, Real):
return float(other) + float(self)
elif isinstance(other, Complex):
return complex(other) + complex(self)
else:
return NotImplemented
三. python数字类型的坑
3.1 浮点数的最大值
在python中要注意的是整数没有最大的限制, 但是浮点数是有最大限制的, 这个值默认为inf, 这个最大值取决于自己的机器
a = 2e300
print(a) # output: 2e300
b = 2e400
print(b) # output: inf
3.2 python浮点数的运算
在计算机的世界中, 由于一切数字和浮点数都是0,1组成的, 所以分数也是用0,1实现, 比如说0.5就是用2的-1次方实现
对比:
十进制的0.125为1/10 + 2/100 + 5/1000
二进制0.111即为1/2 + 1/4 + 1/8;
这两个数字的值可以相同, 但是大多数的值是不能相通的, 比如十进制的0.3, 0.33等
值得一说的是, 虽然我们为某个值赋值为0.1, print也为0.1, 实际上是由浮点数类的repr函数通过控制小数点个数来实现的, 所以, 浮点数的运算其实是不太靠谱的, 除非事后进行舍入
.1 + .1 + .1 == .3 # output: False
round(.1, 1) + round(.1, 1) + round(.1, 1) == round(.3, 1) # output: False
round(.1 + .1 + .1, 10) == round(.3, 10) # output: True
因此, 如果想要好好的进行浮点数的运算, 一般有两种方法:
3.2.1 DECIMAL 类型进行浮点数运算(推荐)
DECIMAL类型在大部分的编程语言和数据库都有支持, 所以可以运行的很好, 唯一的缺点是会带来一些性能上的损耗
from decimal import Decimal
print(Decimal("0.1") + Decimal("0.1") + Decimal("0.1") == Decimal("0.3")) # output: True
3.2.2 使用as_integet_ratio()进行
这个函数可以将浮点数转换为分数模式
a = 0.1
print(a.as_integer_ratio()) # output: (3602879701896397, 36028797018963968)
print(a == 3602879701896397/36028797018963968) # output: True
3.2.3 使用math.fsum()进行运算
这个函数有助于减少求和过程中的精度损失。 它会在数值被添加到总计值的时候跟踪“丢失的位”。 这可以很好地保持总计值的精确度, 使得错误不会积累到能影响结果总数的程度
sum([0.1] * 10) == 1.0 # output: False
math.fsum([0.1] * 10) == 1.0 # output: True
参考文档
https://realpython.com/python-numbers/
https://docs.python.org/3/library/numbers.html?highlight=numbers#module-numbers