因为二进制表示浮点数的特殊性, 浮点数误差在计算机编程中普遍存在, 参考: 浮点数精度问题及解决方案 下面是 Python 语言的浮点数精度问题和解决方案
python 浮点小数精度问题
- 小数的精度问题
1 | # 第一种情况 |
输出:
1 | 0.1 |
- round() 的进位规则问题
在Python 3里面, round
对小数的精确度采用
四舍六入五成双
的方式。比如 1.15—>1.2, 1.25—>1.2,
1.250—>1.2, 1.25012—>1.3
round
给出的结果如果与你设想的不一样,有两个原因:
- 你的这个小数在计算机中能不能被精确储存?如果不能,那么它可能并没有达到四舍五入的标准,例如
1.115
,它的小数点后第三位实际上是4
,当然会被舍去。
- 如果你的这个小数在计算机中能被精确表示,那么,
round
采用的进位机制是奇进偶舍
,所以这取决于你要保留的那一位,它是奇数还是偶数,以及它的下一位后面还有没有数据。
解决方法
- 小数的精度问题 使用 Decimal 表示和计算数字
1
2
3
4
5
6
7
8
9from decimal import Decimal
a = 0.1;
print(a) # 默认精度
print(Decimal('0.1')) # 17位精度
b = Decimal('1.1') + Decimal('2.2')
print(b) - round() 的进位规则问题 自定义round规则
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32from decimal import Decimal, ROUND_HALF_UP
def custom_round(number: str, precision: int = 0):
"""
自定义浮点型四舍五入
:param number: 输入数值
:param precision: 浮点型的精度位数, 可能是负数
:return:
"""
number_decimal = Decimal(number)
number_float = float(number)
sign = 1 # 正负号处理, 默认是正号
if number_decimal < 0: # 符号变更 (Decimal 的 quantize 不支持负数, 需要先转为正数)
sign = -1
number_float = number_float * -1
if precision <= 0:
# 整数或者负精度的数字的处理方式
base = 10 ** precision
big = number_float * base + 0.5 # 先变小'精度*10'倍, 然后 +0.5 进位
restore = int(big) / base
return sign * restore
precision = str(1 / 10 ** precision)
return number_decimal.quantize(Decimal(precision), rounding=ROUND_HALF_UP) * sign
print(f"normal round: {round(3.14159265358979323846, 19)} \tcustom round:{custom_round('3.14159265358979323846', 19)}")
print(f"normal round: {round(-6.25, -1)} \tcustom round:{custom_round('-6.25', -1)}")
print(f"normal round: {round(6.25, -2)}\tcustom round:{custom_round('6.25', -2)}")
- python 数据精度