# 浮点数
# 误差
- 在 Python 解释器的底层,所有对象都以二进制的形式存储。但是,大部分十进制小数,都不能准确转换成二进制。
- 例:以下算法,是将十进制浮点数,转换成二进制数
def float_to_binary(x, precision=16): # 如果 x 为负数,则转换成正数,再处理 prefix = '+' if x < 0: x = -x prefix = '-' # 提取 x 的整数部分 integer_part = int(x) # 整数部分可以直接转换成二进制,没有误差 binary_integer_part = bin(integer_part) # 提取 x 的小数部分 fractional_part = x - int(x) # 第 n 次循环,将十进制小数乘以 2 ,此时个位数取值为 0 或 1 ,将这个值记作第 n 位二进制小数 binary_fractional_part = '' while precision > 0 and fractional_part > 0: fractional_part *= 2 bit = int(fractional_part) binary_fractional_part += str(bit) fractional_part -= bit # 去掉个位数,进入下一次循环 precision -= 1 return prefix + binary_integer_part + '.' + binary_fractional_part
- 转换的示例:
>>> float_to_binary(0.1) # 十进制的 0.1 ,转换成二进制之后,是一个无限循环小数 '+0b0.0001100110011001' >>> float_to_binary(0.2) '+0b0.0011001100110011' >>> float_to_binary(0.3) '+0b0.0100110011001100' >>> float_to_binary(0.4) '+0b0.0110011001100110' >>> float_to_binary(0.5) '+0b0.1'
- 可见,如果 x 反复乘以 2 之后,依然存在小数部分,则说明 x 不能准确转换成二进制。
- 转换的示例:
- 因此,即使浮点数的小数部分不超过 16 位,存储为 float 类型的对象时,也会存在很小的误差。例如:
>>> 0.1 + 0.2 0.30000000000000004 >>> 0.1 * 3 0.30000000000000004
- 因此,如果想对浮点数进行精确的数学运算,可以将它乘以 10^n ,转换为 int 类型的对象。等到显示时,才转换成 float 类型。或者使用 decimal 等模块进行精确运算。
>>> ratio = 10 >>> a = 0.1 >>> a = int(a*ratio) >>> a 1 >>> print(a/ratio) 0.1
# import decimal
:Python 的标准库,用于存储高精度的浮点数。
# 用法
使用
decimal.Decimal
对象可以存储高精度的浮点数。>>> from decimal import Decimal >>> Decimal(-10) Decimal('-10') >>> Decimal('3.14') # 输入可以是 int 或 str 类型的数值 Decimal('3.14') >>> Decimal(3.14) # 将 float 对象直接转换成 Decimal 会有误差,这里应该改为 Decimal(str(3.14)) Decimal('3.140000000000000124344978758017532527446746826171875') >>> str(Decimal('3.14')) # Decimal 对象可以直接转换成字符串 '3.14'
Decimal、int 对象之间可以直接进行运算,结果会返回一个 Decimal 对象。
>>> Decimal('0.1') + Decimal('0.2') Decimal('0.3') >>> Decimal('0.1') + 0.2 # Decimal、float 对象之间不能进行运算 TypeError: unsupported operand type(s) for +: 'decimal.Decimal' and 'float' >>> Decimal('0.1') + 2 Decimal('2.1') >>> 0 / Decimal('0.1') # 被除数为 0 时,结果为 0 Decimal('0E+1') >>> Decimal('0.1') / 0 # 除数为 0 时,抛出异常 decimal.DivisionByZero: [<class 'decimal.DivisionByZero'>] >>> Decimal('3.14') < 3.14 True
Decimal 对象的有效位数默认为 28 。
>>> from decimal import Decimal, getcontext >>> getcontext().prec 28 >>> getcontext().prec = 4 # 设置有效位数 >>> Decimal('0.1') / 3 Decimal('0.03333') >>> Decimal('0.1234567') / 1 # 超出有效位数时,会自动四舍五入 Decimal('0.1235')
# import fractions
:Python 的标准库,用于以分数的形式进行数学运算。
- 官方文档 (opens new window)
- 优点:
- float 在存储时存在微小误差,而分数没有误差。
- 一个很长的浮点数,对应的分数可能很短,方便供人查看。
# 用法
例:创建 Fraction 对象
>>> from fractions import Fraction >>> a = Fraction(1, 3) # 输入分子、分母,创建一个 Fraction 对象 >>> a Fraction(1, 3) >>> a.numerator # 查看分子 1 >>> a.denominator # 查看分母 3 >>> a.numerator = 2 # Fraction 对象是只读的,不允许修改 AttributeError: can't set attribute >>> str(a) # 转换成 str 类型 '1/3' >>> float(a) # 转换成 float 类型 0.3333333333333333 >>> int(a) # 转换成 int 类型,这不会四舍五入,而是只保留 float 的整数部分 0
- 分子、分母必须都是 int 类型,否则抛出异常:
>>> Fraction(1, 1.5) TypeError: both arguments should be Rational instances
- 创建一个 Fraction 对象时,会自动进行约分,将分子、分母同时除以公约数。
>>> Fraction(2, 2) Fraction(1, 1) >>> Fraction(4, 2) Fraction(2, 1) >>> Fraction(1, -2) # 如果分母带有负号,则自动改为分子带负号 Fraction(-1, 2)
- 也可以通过其它方式,创建 Fraction 对象:
>>> Fraction(0.3333) # 输入一个 float 类型的数字 Fraction(6004199023210345, 18014398509481984) >>> Fraction('0.3333') # 输入一个 str 类型的浮点数 Fraction(3333, 10000) >>> Fraction('1/3') # 输入一个 str 类型的分数 Fraction(1, 3)
- 分子、分母必须都是 int 类型,否则抛出异常:
调用
Fraction.limit_denominator()
方法,可以限制分母的最大值:>>> Fraction(0.3333) Fraction(6004199023210345, 18014398509481984) >>> _.limit_denominator(100) # 这会返回一个新的 Fraction 对象,取值接近 0.3333 ,但是分母不超过 100 Fraction(1, 3) >>> _.limit_denominator(3) Fraction(0, 1) >>> _.limit_denominator(2) # 如果分母的取值太小,则取值可能远离 0.3333 Fraction(1, 2) >>> _.limit_denominator(1) # 这里取值变成 0/1=0 Fraction(0, 1) >>> _.limit_denominator(0) ValueError: max_denominator should be at least 1
两个 Fraction 对象之间,可以进行算术运算:
>>> Fraction(1, 2) + Fraction(1, 3) Fraction(5, 6) >>> Fraction(1, 2) * Fraction(1, 3) Fraction(1, 6)