68. decimal与fractions精度控制

一、decimal模块

Decimal类的基本使用

概念定义

Decimal是Python标准库decimal模块提供的类,用于高精度的十进制浮点数运算。与内置的float类型不同,Decimal可以精确表示十进制数,避免了二进制浮点数常见的精度问题。

使用场景
  1. 金融计算(需要精确的货币计算)
  2. 科学计算(需要高精度结果)
  3. 任何需要避免浮点数精度误差的场景
常见误区或注意事项
  1. Decimal对象需要通过字符串初始化才能保证精确性,直接使用浮点数初始化仍会引入精度问题
  2. Decimal运算比float慢,在不需要精确计算的场景不应使用
  3. 需要设置合适的精度(通过getcontext().prec)
示例代码
from decimal import Decimal, getcontext

# 正确初始化方式
a = Decimal('0.1')
b = Decimal('0.2')
print(a + b)  # 输出: 0.3

# 错误初始化方式(仍会有精度问题)
c = Decimal(0.1)
d = Decimal(0.2)
print(c + d)  # 输出: 0.3000000000000000166533453694

# 设置精度
getcontext().prec = 6
e = Decimal('1') / Decimal('7')
print(e)  # 输出: 0.142857

# 金融计算示例
price = Decimal('19.99')
quantity = Decimal('3')
total = price * quantity
print(total)  # 输出: 59.97

精度设置方法(getcontext().prec)

概念定义

getcontext().prec 是 Python 中 decimal 模块提供的用于设置十进制运算精度的属性。它决定了十进制数在进行算术运算时保留的有效数字位数(不包括前导零)。

使用场景
  1. 需要高精度计算的金融、科学计算领域
  2. 避免浮点数精度问题导致的误差
  3. 需要控制计算结果的有效数字位数
注意事项
  1. prec 设置的是有效数字位数,不是小数位数
  2. 设置会影响所有后续的 Decimal 运算
  3. 默认精度为 28 位
  4. 更高的精度会带来更大的计算开销
示例代码
from decimal import getcontext, Decimal

# 设置精度为5位有效数字
getcontext().prec = 5

# 计算结果将保留5位有效数字
result = Decimal(1) / Decimal(7)
print(result)  # 输出: 0.14286

# 设置更高精度
getcontext().prec = 10
result = Decimal(1) / Decimal(7)
print(result)  # 输出: 0.1428571429

舍入模式

概念定义

舍入模式是指在进行数值四舍五入时采用的规则。Python中常用的舍入模式包括:

  • ROUND_HALF_UP:四舍五入(最常用)
  • ROUND_DOWN:直接截断
  • ROUND_UP:远离零方向舍入
  • ROUND_HALF_EVEN:银行家舍入法(四舍六入五成双)
使用场景
  1. 财务计算(需要精确到分)
  2. 科学计算(控制精度)
  3. 数据统计(统一数据格式)
常见误区
  1. 误以为所有语言都默认使用ROUND_HALF_UP
  2. 浮点数精度问题(建议使用Decimal模块)
  3. 混淆不同舍入模式的效果
示例代码
from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_EVEN

# 传统round函数(使用ROUND_HALF_EVEN)
print(round(2.5))  # 2(银行家舍入)
print(round(3.5))  # 4

# 使用Decimal精确控制
num = Decimal('1.235')
print(num.quantize(Decimal('0.01'), ROUND_HALF_UP))  # 1.24
print(num.quantize(Decimal('0.01'), ROUND_HALF_EVEN))  # 1.24

num = Decimal('1.245')
print(num.quantize(Decimal('0.01'), ROUND_HALF_UP))  # 1.25
print(num.quantize(Decimal('0.01'), ROUND_HALF_EVEN))  # 1.24

二、fractions模块

Fraction类的创建方式

概念定义

Fraction类是Python标准库fractions模块中的一个类,用于精确表示分数(有理数)。它可以帮助避免浮点数运算带来的精度问题。

使用场景
  1. 需要精确表示分数(如1/3而不是0.333…)
  2. 财务计算等需要高精度的场景
  3. 数学运算中需要保持分数形式
常见创建方式
  1. 通过分子分母创建
from fractions import Fraction
f = Fraction(3, 4)  # 表示3/4
  1. 通过浮点数创建
f = Fraction(0.5)   # 自动转换为1/2
  1. 通过字符串创建
f = Fraction('3/4')  # 表示3/4
f = Fraction('0.25') # 表示1/4
  1. 通过另一个Fraction对象创建
f1 = Fraction(1, 2)
f2 = Fraction(f1)    # 创建f1的副本
注意事项
  1. 分母不能为0,否则会抛出ZeroDivisionError
  2. 创建时分子分母会自动约分
  3. 从浮点数创建时可能会有微小误差(如Fraction(0.1)不完全等于1/10)
  4. 字符串形式必须符合"分子/分母"或十进制格式
示例代码
from fractions import Fraction

# 不同创建方式示例
print(Fraction(16, -10))  # 输出: -8/5 (自动约分)
print(Fraction(0.25))     # 输出: 1/4
print(Fraction('3/7'))    # 输出: 3/7
print(Fraction('0.1'))    # 输出: 1/10

分数运算与简化

概念定义

分数运算是指对分数进行加、减、乘、除等数学运算的过程。分数简化则是将分数约分到最简形式,即分子和分母没有公因数(除了1)。

使用场景
  1. 在科学计算、工程计算中处理精确值
  2. 游戏开发中处理比例和概率
  3. 金融计算中处理利率和比率
  4. 教育类应用中展示数学运算过程
常见误区或注意事项
  1. 进行加减运算前必须先通分
  2. 约分时要找到最大公约数(GCD)
  3. 除法运算要转换为乘以倒数
  4. 注意处理分母为零的情况
  5. 结果应始终表示为最简分数
示例代码
import fractions
from math import gcd

# 创建分数
f1 = fractions.Fraction(3, 4)
f2 = fractions.Fraction(1, 6)

# 基本运算
print(f1 + f2)  # 11/12
print(f1 - f2)  # 7/12
print(f1 * f2)  # 1/8
print(f1 / f2)  # 9/2

# 手动简化分数
def simplify_fraction(numerator, denominator):
    common_divisor = gcd(numerator, denominator)
    return numerator // common_divisor, denominator // common_divisor

# 示例:简化18/24
simplified = simplify_fraction(18, 24)
print(f"简化后的分数: {simplified[0]}/{simplified[1]}")  # 输出: 3/4

浮点数的转换

概念定义

浮点数转换是指将其他数据类型(如整数、字符串)转换为浮点数(float)类型的过程。在Python中,可以使用float()函数进行转换。

使用场景
  1. 从用户输入(通常是字符串)中获取数值并进行数学计算
  2. 处理包含小数的数据
  3. 需要更高精度的数值运算时
常见误区或注意事项
  1. 不是所有字符串都能转换为浮点数(如"abc"会引发ValueError)
  2. 某些看似简单的浮点数在计算机中可能有精度问题(如0.1+0.2不等于0.3)
  3. 大整数转换为浮点数时可能丢失精度
示例代码
# 字符串转浮点数
num_str = "3.14"
num_float = float(num_str)
print(num_float)  # 输出: 3.14

# 整数转浮点数
num_int = 42
num_float = float(num_int)
print(num_float)  # 输出: 42.0

# 科学计数法转换
sci_num = "1.23e-4"
num_float = float(sci_num)
print(num_float)  # 输出: 0.000123

# 错误示例
try:
    invalid = float("python")
except ValueError as e:
    print(f"错误: {e}")  # 输出: 错误: could not convert string to float: 'python'

三、精度控制对比

decimal与fractions的精度差异

概念定义
  • decimal模块:用于高精度的十进制浮点运算,适用于需要精确小数计算的场景(如金融计算)。
  • fractions模块:用于精确表示分数,避免浮点数运算中的精度损失。
使用场景
  • decimal:适合处理货币、科学计算等需要精确小数位的场景。
  • fractions:适合处理分数运算(如1/3),避免浮点数精度问题。
常见误区
  1. decimal并非完全精确:虽然比float更精确,但仍受限于设置的精度位数。
  2. fractions不适用于所有场景:对于无理数(如π)或复杂小数,fractions可能无法精确表示。
示例代码
from decimal import Decimal, getcontext
from fractions import Fraction

# decimal示例
getcontext().prec = 6  # 设置6位精度
a = Decimal('1.1') + Decimal('2.2')
print(a)  # 输出: 3.3

# fractions示例
b = Fraction(1, 3) + Fraction(1, 6)
print(b)  # 输出: 1/2

适用场景选择

概念定义

适用场景选择是指在编程中根据不同的需求或条件,选择最适合的编程结构、算法或技术方案。在Python中,这通常涉及对多种编程结构(如循环、条件判断、函数等)的选择和优化。

使用场景
  1. 循环选择

    • 当需要遍历已知长度的序列(如列表、元组)时,优先使用for循环。
    • 当需要根据条件重复执行代码块时,优先使用while循环。
  2. 条件判断选择

    • 简单的二元条件判断(如if-else)适合使用if语句。
    • 多重条件判断(如多个elif)适合使用字典映射或match-case(Python 3.10+)。
  3. 函数选择

    • 重复使用的代码块适合封装为函数。
    • 简单的逻辑操作可以使用匿名函数(lambda)。
常见误区或注意事项
  1. 避免过度使用嵌套循环或条件判断,这会导致代码可读性降低。
  2. 不要为了追求简洁而牺牲代码的可读性(如过度使用lambda)。
  3. 在性能敏感的场景中,优先选择时间复杂度更低的算法或数据结构。
示例代码
# 适用场景选择示例:循环选择
numbers = [1, 2, 3, 4, 5]

# 使用for循环遍历列表
for num in numbers:
    print(num)

# 使用while循环实现计数器
count = 0
while count < 5:
    print(count)
    count += 1

# 适用场景选择示例:条件判断选择
value = 10

# 简单的if-else
if value > 5:
    print("大于5")
else:
    print("小于等于5")

# 多重条件判断(使用字典映射替代多个elif)
def handle_case_1():
    print("处理情况1")

def handle_case_2():
    print("处理情况2")

handlers = {
    1: handle_case_1,
    2: handle_case_2,
}

handlers.get(value, lambda: print("默认处理"))()

性能比较

概念定义

性能比较是指通过量化指标(如执行时间、内存占用等)来评估不同代码实现或算法在相同条件下的运行效率差异。在Python中,常用于比较不同实现方式的优劣。

使用场景
  1. 选择最优算法时(如排序算法对比)
  2. 评估不同库的性能差异(如requests vs urllib
  3. 优化关键代码路径前确定瓶颈
  4. 验证代码优化效果时
常见误区
  1. 单次测试:未考虑系统波动,应多次测试取平均值
  2. 忽略环境:未控制测试环境的一致性(如后台进程干扰)
  3. 过早优化:在非关键路径上进行不必要的优化
  4. 错误指标:选择了不相关的比较指标(如用内存比较排序速度)
示例代码
import timeit

# 比较列表生成方式
def test_for_loop():
    return [i for i in range(1000)]

def test_list():
    return list(range(1000))

# 各执行10000次,比较耗时
t1 = timeit.timeit(test_for_loop, number=10000)
t2 = timeit.timeit(test_list, number=10000)

print(f"列表推导式: {t1:.4f}秒")
print(f"list()转换: {t2:.4f}秒")
print(f"性能差异: {abs(t1-t2)/t1*100:.1f}%")
更精确的测试方法
from timeit import repeat

trials = repeat(
    stmt="sorted(data)",
    setup="import random; data=[random.random() for _ in range(1000)]",
    repeat=5,
    number=1000
)
print(f"最佳耗时: {min(trials):.4f}秒")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值