你真的搞懂了Python中的四舍五入吗?1.45保留小数点后1位,四舍五入后得多少?

你真的搞懂了Python中的四舍五入吗?1.45保留小数点后1位,四舍五入后得多少?

先来看看Python内置的四舍五入函数:

round(x, n)
  • 该函数返回传入的x数值(浮点型或整型)的四舍五入值;
  • 参数n表示对x进行四舍五入时保留/参考的数位精度;
  • 参数n可以不传入,此时其默认值为0,但若传入的话,则必须为整型;
  • 当n为0时,返回的结果为整型,n为负整数或正整数时,返回值的数值类型与传入的x数值的类型一样(整型或者浮点型);
  • 若n为正整数,则表示四舍五入时保留/参考的小数点后的位数;
  • 若n为负整数,-1表示基于十位四舍五入(舍弃个位)、-2表示基于百位四舍五入(舍弃个位和十位),依次类推;
  • round返回的是数值(整型或者浮点型),对于数值,我们关注的是准确性,因为数值是用于算术运算的,比如round(31415.0, 2),得到的结果是“31415.0”,是完全符合要求的,既按约定返回了浮点型(因为传入的参数也是浮点型),同时,也完全满足小数点后2位的数位精度(或者说有效位数),因为对于数值来说,“31415.0”与“31415.00”是完全相等的;
  • 但需要注意的是:上述谈到的“数值”的有效位或者精准度,与我们平时提到的“保留多少有效位”,两者的概念是不一样的,或者说至少在编程方面,两者是不同层面的事情,前者关注的是“数值”的精准度,后者用于显示或输出,关注的是对数值的字符串格式化;
  • 因此,若数值四舍五入后,用于算术表达式计算,我们则采用round函数对数值进行四舍五入处理,要是仅用于按要求保留有效位显示输出,则直接采用字符串格式化输出即可,例如:f'{31415:.2f}',输出为“31415.00”。

如下代码所示,各打印了两个数,前者按round调用后返回的真实数值输出,后者按字符串格式化输出。


print('对3.14159265保留小数点后3位、四舍五入的结果:',round(3.14159265, 3), f'{3.14159265:0.3f}')
print('对31415.9265仅保留整数部分、四舍五入的结果:',round(31415.9265), f'{31415.9265:.0f}')
print('对31415.9265基于百位进行四舍五入的结果:',round(31415.9265, -2), f'{round(31415.9265, -2):.0f}')
print('对31415基于百位进行四舍五入的结果:',round(31415, -2), f'{round(31415, -2):.0f}')
print('对31415参考小数点后2位、四舍五入的结果:',round(31415, 2), f'{31415:.2f}')
print('对31415.0参考小数点后2位、四舍五入的结果:',round(31415.0, 2), f'{31415.0:.2f}')

输出结果如下:

对3.14159265保留小数点后3位、四舍五入的结果: 3.142 3.142
对31415.9265仅保留整数部分、四舍五入的结果: 31416 31416
对31415.9265基于百位进行四舍五入的结果: 31400.0 31400
对31415基于百位进行四舍五入的结果: 31400 31400
对31415参考小数点后2位、四舍五入的结果: 31415 31415.00
对31415.0参考小数点后2位、四舍五入的结果: 31415.0 31415.00

另外,更重要的,对于Python的这个round内置函数我们在使用时要需要注意以下两点:

  • 四舍五入运算必然涉及到进位或者不进位,比如,我们对数值x(如1.45)进行四舍五入运算,假设进位得到A(如1.5,保留小数点后1位)、若不进位得到B(如1.4,保留小数点后1位),有时候会出现一种特别情况,x正好在A与B的中间,即离A和B的距离相同,这种情况下一般是碰到了“5”,此时,究竟取A还是取B呢?Python的处理是取“偶数”(如,1.4的最后一位4为偶数),跟我们传统的“五入”做法是不一样的。如下所示:


print('为了促成偶数,放弃“五入”:',round(0.5),round(-0.5),round(2.5),round(125,-1),round(1.45,1))
print('正好促成偶数,保留“五入”:',round(1.5),round(-1.5),round(1.5),round(135,-1),round(1.35,1))

输出结果如下:

为了促成偶数,放弃“五入”: 0 0 2 120 1.4
正好促成偶数,保留“五入”: 2 -2 2 140 1.4
  • 而且上述规则还存在另外的挑战,当碰到了“5”且又遇上了特别的浮点数时,情况会变得更加复杂,例如,round(6.675, 2)给出的结果是6.67,而不是按上述规则应该得出的6.68,这并不是Python语言的Bug或问题,而是因为:计算机本身对浮点数进行十进制至二进制转换时,存在着一些十进制小数不能以二进制精确地表示出来(相当于除法中除不断的情况),而计算机用来存储数字的二进制位数是有限的,必然导致后面的数位被舍弃,于是,x(比如,当前的6.675)不再是我们十进制中看到的,离A(即6.68)和B(即6.67)的距离完全相等,而是变小了些,离B更近了,于是被round函数选中了6.67。


print('受计算机精度影响的四舍五入:',round(6.675,2),round(1.355,2))

输出结果如下:

受计算机精度影响的四舍五入: 6.67 1.35

需要说明的是,上述两点特殊情况,对于数值的字符串格式化会产生同样的影响,知道了上述规则和原因后,遇到类似的特殊情况,我们就不会再感到不解或者迷惑了,甚至在特殊的严格场合,我们可能需要进行特殊处理,规避上述情况的发生。如下所示,我们来看看字符串格式化对上述特殊数值的输出情况。


print('旧式格式化操作符:')
print('%.0f %.1f %.2f' % (-0.5,1.45,6.675))
print('%.0f %.1f' % (-1.5,1.35))

print('\nstr.format()的格式化:')
print('{:.0f} {:.1f} {:.2f}'.format(-0.5,1.45,6.675))
print('{:.0f} {:.1f}'.format(-1.5,1.35))

print('\nf-string格式化:')
print(f'{-0.5:.0f} {1.45:.1f} {6.675:.2f}')
print(f'{-1.5:.0f} {1.35:.1f}')

输出结果如下:

旧式格式化操作符:
-0 1.4 6.67
-2 1.4

str.format()的格式化:
-0 1.4 6.67
-2 1.4

f-string格式化:
-0 1.4 6.67
-2 1.4

发布于 2019-11-23

文章被以下专栏收录

    微信公众号同名,欢迎投稿。全平台约20万开发者关注,会员来自全球十多个国家和地区,拥有十多个线上线下技术社群,向本专栏投稿即默认发布到Python中文社区全平台。Telegram:t.me/PyChina