HoRain云--Python小数点处理7大技巧

🎬 HoRain 云小助手:个人主页
⛺️生活的理想,就是为了理想的生活!
⛳️ 推荐
前些天发现了一个超棒的服务器购买网站,性价比超高,大内存超划算!忍不住分享一下给大家。点击跳转到网站。
目录
2. f-string格式化 - Python 3.6+推荐

在Python中控制小数点位数有多种方法,每种方法适用于不同场景。下面我将详细介绍7种主要方法,并提供最佳实践建议。
📊 方法对比总览
|
方法 |
核心函数/语法 |
适用场景 |
返回值类型 |
舍入规则 |
精度控制 |
|---|---|---|---|---|---|
|
round() |
|
四舍五入计算 |
float/int |
银行家舍入 |
中等 |
|
格式化字符串 |
|
显示格式化 |
str |
四舍六入五成双 |
高 |
|
format() |
|
通用格式化 |
str |
四舍六入五成双 |
高 |
|
Decimal模块 |
|
精确计算 |
Decimal |
多种可选 |
非常高 |
|
字符串格式化% |
|
传统格式化 |
str |
四舍六入五成双 |
高 |
|
numpy.around |
|
数组批量处理 |
ndarray |
四舍六入五成双 |
高 |
|
手动截断 |
|
向下/截断舍入 |
float |
指定方向 |
中等 |
🔧 7种核心方法详解
1. round()函数 - 基本四舍五入
# 基本用法
num = 3.1415926535
# 保留2位小数
result = round(num, 2) # 3.14
print(f"round({num}, 2) = {result}")
# 保留3位小数
result = round(num, 3) # 3.142
print(f"round({num}, 3) = {result}")
# 注意:round()的特殊舍入规则(银行家舍入)
print(round(2.675, 2)) # 输出: 2.67 而不是 2.68
print(round(1.5)) # 输出: 2
print(round(2.5)) # 输出: 2 (银行家舍入:向最近的偶数舍入)
# 处理负数
print(round(-1.5)) # 输出: -2
print(round(-2.5)) # 输出: -2
注意事项:
-
ndigits参数可以是负数,表示舍入到十位、百位等 -
返回类型:如果
ndigits省略或为None,返回int,否则返回float -
浮点数精度问题可能导致意外结果
2. f-string格式化 - Python 3.6+推荐
value = 123.456789
# 基本格式化
print(f"原始值: {value}")
print(f"两位小数: {value:.2f}") # 123.46
print(f"四位小数: {value:.4f}") # 123.4568
print(f"零填充: {value:08.2f}") # 00123.46
print(f"总宽10位: {value:10.2f}") # ' 123.46'
print(f"左对齐: {value:<10.2f}|") # '123.46 |'
# 科学计数法
print(f"科学计数: {value:.2e}") # 1.23e+02
# 百分比格式
percent = 0.8567
print(f"百分比: {percent:.1%}") # 85.7%
print(f"百分比: {percent:.2%}") # 85.67%
# 千分位分隔
large_num = 1234567.8912
print(f"千分位: {large_num:,.2f}") # 1,234,567.89
# 组合使用
print(f"格式化: ¥{large_num:,.2f}") # ¥1,234,567.89
3. format()函数 - 灵活通用
value = 123.456789
# 方法1: 使用format()函数
print(format(value, '.2f')) # '123.46'
print(format(value, '.4f')) # '123.4568'
print(format(value, '10.2f')) # ' 123.46'
print(format(value, '0>10.2f')) # '0000123.46'
# 方法2: 在str.format()中使用
print("值: {:.2f}".format(value)) # 值: 123.46
print("值: {:>10.2f}".format(value)) # 值: ' 123.46'
print("值: {:_>10.2f}".format(value)) # 值: '____123.46'
# 命名参数
print("X: {x:.3f}, Y: {y:.3f}".format(x=1.2345, y=5.6789))
# X: 1.235, Y: 5.679
# 位置参数
print("第一个: {0:.2f}, 第二个: {1:.3f}".format(1.2345, 5.6789))
# 第一个: 1.23, 第二个: 5.679
4. Decimal模块 - 精确计算
from decimal import Decimal, ROUND_HALF_UP, ROUND_DOWN, ROUND_CEILING, ROUND_FLOOR
# 创建Decimal对象
d = Decimal('123.456789')
# 1. 使用quantize()方法精确控制
# 保留2位小数,四舍五入
result = d.quantize(Decimal('0.00'), rounding=ROUND_HALF_UP)
print(f"四舍五入: {result}") # 123.46
# 保留3位小数,向下舍入
result = d.quantize(Decimal('0.000'), rounding=ROUND_DOWN)
print(f"向下舍入: {result}") # 123.456
# 2. 创建上下文控制全局精度
from decimal import getcontext, Context
# 设置全局精度
getcontext().prec = 4 # 总位数(不包括小数点)
d1 = Decimal('1.23456789')
d2 = Decimal('2.3456789')
print(f"加法: {d1 + d2}") # 3.580
# 3. 临时上下文
with decimal.localcontext() as ctx:
ctx.prec = 6
ctx.rounding = ROUND_HALF_UP
result = Decimal('1.23456789') + Decimal('2.3456789')
print(f"临时上下文: {result}") # 3.58025
# 4. 不同舍入模式示例
test_num = Decimal('123.456789')
modes = {
'ROUND_UP': '远离零方向舍入',
'ROUND_DOWN': '向零方向舍入',
'ROUND_CEILING': '向正无穷方向舍入',
'ROUND_FLOOR': '向负无穷方向舍入',
'ROUND_HALF_UP': '四舍五入',
'ROUND_HALF_DOWN': '五舍六入',
'ROUND_HALF_EVEN': '银行家舍入'
}
for mode_name, description in modes.items():
mode = getattr(decimal, mode_name)
result = test_num.quantize(Decimal('0.00'), rounding=mode)
print(f"{mode_name:20} ({description:10}): {result}")
5. 传统%格式化
value = 123.456789
# 基本格式化
print("%.2f" % value) # 123.46
print("%.4f" % value) # 123.4568
print("%10.2f" % value) # ' 123.46'
print("%010.2f" % value) # '0000123.46'
# 多个值
x, y = 1.2345, 5.6789
print("X: %.2f, Y: %.3f" % (x, y)) # X: 1.23, Y: 5.679
# 字典传递
print("值: %(num).2f" % {'num': value}) # 值: 123.46
# 科学计数法
print("%.2e" % value) # 1.23e+02
6. numpy.around - 数组处理
import numpy as np
# 单个值
print(np.around(3.14159, 2)) # 3.14
print(np.around(3.14159, 3)) # 3.142
# 数组处理
arr = np.array([1.234567, 2.345678, 3.456789])
print(np.around(arr, 2)) # [1.23 2.35 3.46]
print(np.around(arr, 3)) # [1.235 2.346 3.457]
# 设置输出格式
np.set_printoptions(precision=3, suppress=True)
print(arr) # [1.235 2.346 3.457]
# 不同舍入函数
arr = np.array([1.234, 2.345, 3.456, 4.567])
print("原始数组:", arr)
print("around (四舍五入):", np.around(arr, 2))
print("floor (向下取整):", np.floor(arr))
print("ceil (向上取整):", np.ceil(arr))
print("trunc (截断):", np.trunc(arr))
print("rint (最近整数):", np.rint(arr))
# 格式化输出整个数组
formatted = [f"{x:.2f}" for x in arr]
print("格式化数组:", formatted)
7. 手动截断与舍入
import math
def truncate_float(number, digits):
"""截断小数位,不进行四舍五入"""
multiplier = 10 ** digits
return math.trunc(number * multiplier) / multiplier
def round_half_up(number, digits):
"""实现真正的四舍五入(非银行家舍入)"""
multiplier = 10 ** digits
return math.floor(number * multiplier + 0.5) / multiplier
# 测试
num = 123.456789
print(f"原始值: {num}")
print(f"截断2位: {truncate_float(num, 2)}") # 123.45
print(f"截断3位: {truncate_float(num, 3)}") # 123.456
print(f"四舍五入2位: {round_half_up(num, 2)}") # 123.46
print(f"四舍五入3位: {round_half_up(num, 3)}") # 123.457
# 比较银行家舍入和四舍五入
test_cases = [1.5, 2.5, 3.5, 4.5]
for n in test_cases:
print(f"{n}: round()={round(n)}, 四舍五入={round_half_up(n, 0)}")
# 1.5: round()=2, 四舍五入=2
# 2.5: round()=2, 四舍五入=3
# 3.5: round()=4, 四舍五入=4
# 4.5: round()=4, 四舍五入=5
🎯 实战应用场景
场景1:财务计算(精确舍入)
from decimal import Decimal, ROUND_HALF_UP, getcontext
class FinancialCalculator:
def __init__(self, precision=2):
self.precision = precision
getcontext().rounding = ROUND_HALF_UP
def calculate_total(self, prices, tax_rate=0.13):
"""计算含税总价,精确到分"""
# 使用Decimal避免浮点误差
total = Decimal('0')
tax_rate_dec = Decimal(str(tax_rate))
for price in prices:
total += Decimal(str(price))
# 计算税额,四舍五入到分
tax = (total * tax_rate_dec).quantize(Decimal('0.01'))
total_with_tax = (total + tax).quantize(Decimal('0.01'))
return {
'subtotal': float(total.quantize(Decimal('0.01'))),
'tax': float(tax),
'total': float(total_with_tax)
}
def format_currency(self, amount):
"""格式化货币显示"""
return f"${float(Decimal(str(amount)).quantize(Decimal('0.00'))):,.2f}"
# 使用示例
calculator = FinancialCalculator(precision=2)
prices = [19.99, 29.99, 9.99, 4.99]
result = calculator.calculate_total(prices, tax_rate=0.13)
print("明细:")
for i, price in enumerate(prices, 1):
print(f"商品{i}: {calculator.format_currency(price)}")
print(f"\n小计: {calculator.format_currency(result['subtotal'])}")
print(f"税额: {calculator.format_currency(result['tax'])}")
print(f"总计: {calculator.format_currency(result['total'])}")
场景2:科学数据分析
import numpy as np
import pandas as pd
class ScientificDataFormatter:
def __init__(self, precision=4, scientific_threshold=1e4):
self.precision = precision
self.scientific_threshold = scientific_threshold
def format_number(self, num):
"""智能格式化数字"""
if isinstance(num, (np.ndarray, list)):
return np.array([self.format_number(x) for x in num])
abs_num = abs(num)
if abs_num == 0:
return f"0.{'0' * self.precision}"
elif abs_num < 1e-3 or abs_num > self.scientific_threshold:
# 科学计数法
return f"{num:.{self.precision}e}"
else:
# 普通小数
return f"{num:.{self.precision}f}"
def format_dataframe(self, df, columns=None):
"""格式化DataFrame中的数值列"""
df_formatted = df.copy()
if columns is None:
columns = df.select_dtypes(include=[np.number]).columns
for col in columns:
df_formatted[col] = df_formatted[col].apply(
lambda x: self.format_number(x) if pd.notnull(x) else x
)
return df_formatted
# 使用示例
formatter = ScientificDataFormatter(precision=3)
# 创建示例数据
data = {
'测量值': [0.000123456, 1234.5678, 9876543.21, 0],
'误差': [0.000000987, 0.123456, 123.456, 0],
'百分比': [0.123456, 99.987654, 100.123456, 0]
}
df = pd.DataFrame(data)
print("原始数据:")
print(df)
print("\n格式化后:")
print(formatter.format_dataframe(df))
场景3:报表生成
from typing import List, Dict, Union
from dataclasses import dataclass
@dataclass
class ReportColumn:
name: str
width: int
precision: int = 2
align: str = 'right'
thousands_sep: bool = False
currency: bool = False
class ReportGenerator:
def __init__(self, title: str, columns: List[ReportColumn]):
self.title = title
self.columns = columns
self.rows = []
def add_row(self, row_data: Dict):
self.rows.append(row_data)
def _format_value(self, value, column: ReportColumn) -> str:
"""格式化单个值"""
if value is None:
return 'N/A'
if isinstance(value, (int, float)):
# 数值格式化
format_str = f",.{column.precision}f" if column.thousands_sep else f".{column.precision}f"
if column.currency:
formatted = f"${value:{format_str}}"
else:
formatted = f"{value:{format_str}}"
# 对齐处理
if column.align == 'right':
return formatted.rjust(column.width)
elif column.align == 'center':
return formatted.center(column.width)
else:
return formatted.ljust(column.width)
else:
# 非数值
return str(value).ljust(column.width)
def generate(self) -> str:
"""生成报表字符串"""
lines = []
# 标题
total_width = sum(col.width for col in self.columns) + len(self.columns) - 1
lines.append("=" * total_width)
lines.append(self.title.center(total_width))
lines.append("=" * total_width)
# 表头
header_parts = []
for col in self.columns:
if col.align == 'right':
header_parts.append(col.name.rjust(col.width))
elif col.align == 'center':
header_parts.append(col.name.center(col.width))
else:
header_parts.append(col.name.ljust(col.width))
lines.append(" ".join(header_parts))
lines.append("-" * total_width)
# 数据行
for row in self.rows:
row_parts = []
for col in self.columns:
value = row.get(col.name, None)
row_parts.append(self._format_value(value, col))
lines.append(" ".join(row_parts))
lines.append("=" * total_width)
# 汇总行
if self.rows:
summary_parts = []
for col in self.columns:
if col.name == 'amount':
# 计算总额
total = sum(row.get('amount', 0) for row in self.rows)
summary_parts.append(self._format_value(total, col))
elif col.name == 'description':
summary_parts.append("总计".ljust(col.width))
else:
summary_parts.append(" " * col.width)
lines.append(" ".join(summary_parts))
return "\n".join(lines)
# 使用示例
columns = [
ReportColumn("id", 5, 0, 'right'),
ReportColumn("description", 20, align='left'),
ReportColumn("quantity", 10, 0, 'right'),
ReportColumn("price", 12, 2, 'right', thousands_sep=True, currency=True),
ReportColumn("amount", 12, 2, 'right', thousands_sep=True, currency=True)
]
generator = ReportGenerator("销售报表", columns)
generator.add_row({
'id': 1,
'description': '笔记本电脑',
'quantity': 2,
'price': 4599.99,
'amount': 9199.98
})
generator.add_row({
'id': 2,
'description': '无线鼠标',
'quantity': 5,
'price': 129.50,
'amount': 647.50
})
generator.add_row({
'id': 3,
'description': '机械键盘',
'quantity': 3,
'price': 399.00,
'amount': 1197.00
})
print(generator.generate())
⚡ 性能对比
import timeit
import random
from decimal import Decimal
def performance_test():
"""性能对比测试"""
# 生成测试数据
test_data = [random.uniform(0, 10000) for _ in range(10000)]
tests = {
'round': lambda: [round(x, 2) for x in test_data],
'f-string': lambda: [f"{x:.2f}" for x in test_data],
'format_func': lambda: [format(x, '.2f') for x in test_data],
'decimal': lambda: [str(Decimal(str(x)).quantize(Decimal('0.00'))) for x in test_data],
'percent_format': lambda: ["%.2f" % x for x in test_data]
}
print("性能测试结果 (处理10000个浮点数):")
print("=" * 60)
results = {}
for name, func in tests.items():
time_taken = timeit.timeit(func, number=10)
results[name] = time_taken
print(f"{name:15} {time_taken:.4f} 秒")
# 找出最快的方法
fastest = min(results, key=results.get)
print(f"\n最快方法: {fastest}")
return results
# 运行性能测试
performance_test()
💎 最佳实践总结
1. 选择指南
|
使用场景 |
推荐方法 |
理由 |
|---|---|---|
|
简单显示 |
f-string ( |
简洁、可读性好、性能高 |
|
精确计算 |
Decimal模块 |
避免浮点误差,控制舍入方式 |
|
批量处理 |
numpy.around |
向量化操作,性能好 |
|
向下/截断 |
math.trunc/floor |
明确控制舍入方向 |
|
传统代码 |
%格式化 |
兼容旧代码 |
|
数据格式化 |
format()函数 |
灵活控制格式 |
|
通用四舍五入 |
round()函数 |
内置函数,简单 |
2. 重要注意事项
-
浮点数精度问题:
# 问题 print(0.1 + 0.2) # 0.30000000000000004 # 解决方案 from decimal import Decimal print(Decimal('0.1') + Decimal('0.2')) # 0.3 -
银行家舍入陷阱:
# round()使用银行家舍入 print(round(1.5)) # 2 print(round(2.5)) # 2 ← 注意这里! # 需要真正四舍五入时 def round_half_up(num, digits=0): multiplier = 10 ** digits return math.floor(num * multiplier + 0.5) / multiplier -
格式化与舍入的区别:
num = 1.235 # 格式化(显示用) formatted = f"{num:.2f}" # "1.24" (字符串) # 舍入(计算用) rounded = round(num, 2) # 1.24 (浮点数) -
全局精度控制:
import sys sys.float_repr_style = 'short' # 控制浮点数显示 # 或者使用numpy import numpy as np np.set_printoptions(precision=3, suppress=True)
3. 实用工具函数
def format_number(value, precision=2, style='decimal',
thousands_sep=True, currency=False):
"""通用数字格式化函数"""
if value is None:
return ''
if style == 'percent':
return f"{value:.{precision}%}"
elif style == 'scientific':
return f"{value:.{precision}e}"
elif style == 'currency':
format_str = f",.{precision}f" if thousands_sep else f".{precision}f"
return f"${value:{format_str}}"
else: # decimal
format_str = f",.{precision}f" if thousands_sep else f".{precision}f"
return f"{value:{format_str}}"
# 使用示例
print(format_number(1234.5678, 2, 'currency')) # $1,234.57
print(format_number(0.1234, 1, 'percent')) # 12.3%
print(format_number(1234.5678, 2, 'decimal')) # 1,234.57
根据具体需求选择合适的方法,可以避免很多常见的精度和格式化问题。记住:显示用格式化,计算用舍入,精确用Decimal。
❤️❤️❤️本人水平有限,如有纰漏,欢迎各位大佬评论批评指正!😄😄😄
💘💘💘如果觉得这篇文对你有帮助的话,也请给个点赞、收藏下吧,非常感谢!👍 👍 👍
🔥🔥🔥Stay Hungry Stay Foolish 道阻且长,行则将至,让我们一起加油吧!🌙🌙🌙
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐




所有评论(0)