环境
Python 3.7
描述
在 Python 3 中,round() 函数采用银行家的思维“四舍六入五成双 银行家式舍入法”,而不是使用人类常用的思维“四舍五入”。同时还有浮点数在计算机中进行二进制存放问题,导致在使用 round() 函数时,会出现“感觉上很奇怪的舍入法”。
银行家式舍入法 四舍六入五成双
四舍六入五考虑。
五后非零就进一,
五后皆零看奇偶,
五前为偶应舍去,
五前为奇要进一。
例子
一个数 a.bcdef,需要精确到小数点后两位,那么就要看小数点后第三位:
1.如果 d 小于 5,直接舍去
2.如果 d 大于 5,直接进位
3.如果 d 等于 5:
3.1 且 d 后面没有数据,且 c 为偶数,那么不进位,保留 c
3.2 且 d 后面没有数据,且 c 为奇数,那么进位,c 变成(c + 1)
3.3 且 d 后面还有非 0 数字,此时一定要进位,c 变成(c + 1)
感觉上很奇怪的舍入法
1.315,采用“银行家式舍入法”,应该为多少?
分析
1.315,采用“银行家式舍入法”,保留两位小数,则应看看小数点第二(奇数)、三(等于5)和四位(无数据),则应执行“3.2”,则应结果应为:1.32
理论(感觉):1.32
实际
执行代码:
round(1.315, 2)
实际:1.31
为什么?
使用 Decimal(1.315) 查看 1.315 在人类眼中实际的值(十进制)。
发现 1.315 二进制转化时是有精度损失,所以实际是:1.314999999999999946709294817992486059665679931640625,导致在进行 round(1.315)时,实际执行为 round(1.31499...),则出现 1.31,而不是 1.32。
结束语
四舍五入是针对十进制的,在十进制与二进制转换中会有精度损失,再使用银行家式舍入法后,产生了感觉上奇怪的舍入法。
四舍五入代码
此函数实现了简单的四舍五入,只适用于有小数点的数且并不完善。
第一个参数:要进行四舍五入的数字
第二个参数:要保留小数点后几位
from decimal import Decimal, Context, ROUND_HALF_UP
def sswr(num, accuracy):
str_num = str(num)
zs_len = len(str_num.split('.')[0])
fh = Context(prec=zs_len + accuracy, rounding=ROUND_HALF_UP).create_decimal(str_num)
return fh
参考
https://segmentfault.com/a/1190000018929994
https://blog.csdn.net/qq_34979346/article/details/83827044
我也是无意中发现这个奇怪的事情,0.045怎么用numpy和pandas的round保留两位小数都是0.04。
但是你的例子怎么是用5前面是奇数做例子还舍了呢?
你说的是“1.315”这个吗? 对这个进行保留两位小数,而在进行运算的时候,会进行二进制转换,导致在计算机看来是对(二进制转换后)的“1.31499999...”数字进行保留两位小数操作。
所以 1.314,第三位是小于 5 的,直接舍去了。