需求:假定有一个小数,记作abc.defghhijk,其中a、b、c、d、e等代表0-9之间的数字,比如值可以是3.1415926354975。
目的:①以“截断”方式得到指定小数位的小数,②不进行舍入,③而且展示时需要有指定小数位数。
查阅了不少资料,遇到很多问题,这里记录解决的过程,分为3个部分。
1、现有的几个常见用法
2、踩坑
3、自己的最佳实践
一、现有的几个常见用法
1、python内置的round函数
In [107]: round(3.124, 2)
Out[107]: 3.12
In [108]: round(3.125, 2)
Out[108]: 3.12
In [109]: round(3.126, 2)
Out[109]: 3.13
结果:round函数会舍入处理,一般用于精确度要求不高的场景。
说明:round的“舍入”进位方式准确点应该叫“四舍六入五成双”,不是简单的四舍五入
2、字符串格式化
print("{:.2f}".format(3.124))
print("{:.2f}".format(3.125))
print("{:.2f}".format(3.126))
3.12
3.12
3.13
print("%.2f" % 3.124)
print("%.2f" % 3.125)
print("%.2f" % 3.126)
3.12
3.12
3.13
print(f"{3.124:.2f}")
print(f"{3.125:.2f}")
print(f"{3.126:.2f}")
3.12
3.12
3.13
结果:不同的字符串格式化方式均会舍入处理
3、decimal模块
decimal是Python核心库用于实现高精度小数运算模块。对比原生的float类型,Decimal类型能更好地保证计算精度,特别是货币计算或涉及其他高精度计算的场景。
首先导入decimal模块
from decimal import Decimal, ROUND_HALF_UP
rounding参数为ROUND_HALF_UP
In [127]: Decimal("3.124").quantize(Decimal("0.00"), rounding=ROUND_HALF_UP)
Out[127]: Decimal('3.12')
In [128]: Decimal("3.125").quantize(Decimal("0.00"), rounding=ROUND_HALF_UP)
Out[128]: Decimal('3.13')
In [129]: Decimal("3.126").quantize(Decimal("0.00"), rounding=ROUND_HALF_UP)
Out[129]: Decimal('3.13')
rounding参数缺省时,
In [130]: Decimal("3.124").quantize(Decimal("0.00"))
Out[130]: Decimal('3.12')
In [131]: Decimal("3.125").quantize(Decimal("0.00"))
Out[131]: Decimal('3.12')
In [132]: Decimal("3.126").quantize(Decimal("0.00"))
Out[132]: Decimal('3.13')
结果:会进行“舍入”处理,但当末尾为5时不进位,区别于rounding参数为ROUND_HALF_UP,末尾为5时会进位。
4、使用Numpy库的around函数
Numpy库广泛用于数据处理和科学计算领域,适用于高精度和复杂计算的场景,例如图像处理、机器学习和物理模拟等。
首先导入Numpy模块
import numpy as np
看下around的官方说明:
Signature: np.around(a, decimals=0, out=None)
Docstring:
Evenly round to the given number of decimals.
翻译就是:参数a表示需要保留小数位数的数组或数字,参数decimals表示要保留的小数位数
In [138]: np.around(3.124, 2)
Out[138]: 3.12
In [139]: np.around(3.125, 2)
Out[139]: 3.12
In [140]: np.around(3.126, 2)
Out[140]: 3.13
结果:会进行“舍入”处理
二、踩坑:
1、前4个常见方法(round函数、字符串格式化、decimal模块、numpy.around函数)均会“舍入”处理,不满足要求“截断”目的。
2、round函数、np.around,不仅会“舍入”操作,还会舍弃结果的末尾0。(想要的是3.10,但返回结果是3.1,舍弃了末尾的0。)
示例如下:
In [156]: round(3.103, 2)
Out[156]: 3.1
In [157]: "{:.2f}".format(3.103)
Out[157]: '3.10'
In [158]: "%.2f" % 3.103
Out[158]: '3.10'
In [159]: f"{3.103:.2f}"
Out[159]: '3.10'
In [160]: Decimal("3.103").quantize(Decimal("0.00"), rounding=ROUND_HALF_UP)
Out[160]: Decimal('3.10')
In [165]: np.around(3.103, 2)
Out[165]: 3.1
三、自己的最佳实践
思路:转为字符串进行序列切片,切出小数点前的数值、小数点后的指定位数值,最后拼装。
下面是自己实现的一个保留指定小数位的函数。
def custom_quantize(number, n_digits=0):
""" 截断处理,不舍入 """
number_lst = str(number).split(".")
if n_digits > 0:
if "." in str(number):
new_number = number_lst[0] + "." + number_lst[1][:n_digits]
else:
new_number = number_lst[0] + "." + "0" * n_digits
else:
new_number = number_lst[0]
return new_number
1、测试会不会“舍入”处理
In [135]: custom_quantize(3.124, 2)
Out[135]: 3.12
In [136]: custom_quantize(3.125, 2)
Out[136]: 3.12
In [137]: custom_quantize(3.126, 2)
Out[137]: 3.12
结果为不进行“舍入”处理
2、测试会不会保留末尾的数值0。
In [168]: custom_quantize(3.104, 2)
Out[168]: '3.10'
In [169]: custom_quantize(3.105, 2)
Out[169]: '3.10'
In [170]: custom_quantize(3.106, 2)
Out[170]: '3.10'
切片方式保留末尾的数值0。
本文原始版本发表链接:
kelly会在公众号「kelly学技术」不定期更新文章,感兴趣的朋友可以关注一下,期待与您交流。
--over--