python之关于round函数的bug
round()函数:此函数慎⽤
作⽤:返回浮点数x的“奇进偶舍值”。
round() ⽅法的语法:
round( x [, n] )
参数:
x – 数值表达式。
n – 数值表达式,表⽰保留⼩数点后的位数。
返回值:
返回浮点数x的“奇进偶舍值”。
注意:-------------- 该⽅法和python版本有关。
阅读python的⽂档,⾥⾯是这么写的:
在python2.7的doc中,round()的最后写着,“Values are rounded to the closest multiple of 10 to the power minus ndigits; if two multiples are equally close, rounding is done away from 0.” 保留值将保留到离上⼀位更近的⼀端(四舍六⼊),如果距离两端⼀样远(即要舍去或者进位的那⼀位是五),则保留到离0远的⼀边。所以round(0.5)会近似到1,⽽round(-0.5)会近似到-1。
但是到了python3.5的doc中,⽂档变成了"values are rounded to the closest multiple of 10 to the power minus ndigits; if two multiples are equally close, rounding is done toward the even choice." 如果距离两端⼀样远,会保留到偶数的⼀边。⽐如
round(0.5)和round(-0.5)都会保留到0,⽽round(1.5)会保留到2。
奇进偶舍: ⼜称为四舍六⼊五成则、银⾏进位法(Banker’s Rounding),是⼀种计数保留法,是⼀种数值修约规则(在进⾏具体的数字运算前,通过省略原数值的最后若⼲位数字,调整保留的末位数字,使最后所得到的值最接近原数值的过程。)。从统计学的⾓度,“奇进偶舍”⽐“四舍五⼊”更为精确:在⼤量运算时,因为舍⼊后的结果有的变⼤,有的变⼩,更使舍⼊后的结果误差均值趋于零。⽽不是像四舍五⼊那样逢五就进位,导致结果偏向⼤数,使得误差产⽣积累进⽽产⽣系统误差。“奇进偶舍”使测量结果受到舍⼊误差的影响降到最低。
我们发现,使⽤round()取整⼩数时,并不是想要的四舍五⼊,原因就在于取整规则是采⽤了奇进偶舍(四舍六⼊五成双)的⽅式,简单来说就是:整数部分为奇数,四舍五⼊;如果是偶数,就采⽤五舍六⼊的⽅式,⽽这个规则,就属于数值修约的规则。
下⾯我们⽤实例演⽰:
a =round(1.4)
b =round(1.5)
c =round(1.6)
d =round(2.4)
e =round(2.5)
f =round(2.6)
print('整数部分为奇数时:',a , b, c )
print('整数部分为偶数时:',d , e, f )
运⾏结果:
整数部分为奇数时:122
整数部分为奇数时:223
由以上结果发现,round函数在保留到整数时确实使⽤的奇进偶舍⽅法。
但是: 上⾯我们看到了浮点数去掉⼩数变成整数时是奇进偶舍法,下⾯来看看浮点数不保留到整数时是不是还是奇进偶舍法。
a =round(1.45,1)
b =round(2.45,1)
c =round(1.15,1)
d =round(2.15,1)
print(a)
print(b)
print(c)
print(d)
运⾏结果:
1.4
2.5
1.1
2.1
————>发现了什么是不是跟你想的不⼀样
来分析分析:
如果round函数在不保留到整数的情况下:
假设1:如果它使⽤的是四舍五⼊法呢,结果是什么??----> a = 1.5,b=2.5,c=1.2,d=2.2;显然,结果不对啊,这种假设pass;
假设2:如果它使⽤的是奇进偶舍法呢,即整数部分为奇数,四舍五⼊;整数部分为偶数,就采⽤五舍六⼊的⽅式,结果是什么??----> a = 1.5,b=2.4,c=1.2,d=2.1;显然,结果也不对啊,这种假设pass;
假设3:如果它使⽤的是奇进偶舍法呢,但是,不是看的是整数部分,⽽是看保留到的那⼀位(上例⼦中看⼩数点后⼀位)该位为奇数,四舍五⼊;该位为偶数,就采⽤五舍六⼊的⽅式,结果是什么??----> a = 1.4,b=2.4,c=1.2,d=2.2;显然,结果也不对啊,这种假设还是pass;
四舍五入函数保留整数那到底为什么会出现这种情况呢?原因在此:
这跟浮点数的精度有关。我们知道在机器中浮点数不⼀定能精确表达,在计算机中存⼊的是⼆进制数(0和1),因为⼀些浮点数在换算成⼀串1和0后可能是⽆限位的,机器已经做出了截断处理。那么在机器中保存的这个数字就⽐实际数字要⼩或者⼤。这就导致了结果不准确的原因。
对于该原因,还有⼀个很著名的例⼦,那就是:
python3中:0.1+0.2的结果不等于0.3
此处我⽤的是python3.6.6
a =0.1
b =0.2
print(a + b)
print(0.1+0.2)
print(eval('0.1 + 0.2'))
运⾏结果:
0.30000000000000004
0.30000000000000004
0.30000000000000004
是不是很意外,同样的例⼦还有:
a =0.6
b =0.7
print(a + b)
运⾏结果:
1.2999999999999998
a =0.8
b =0.9
print(a + b)
运⾏结果:
1.7000000000000002
那么,除⾮对精确度没什么要求,否则尽量避开⽤round()函数,所以round()函数要慎⽤
近似计算我们还有其他的选择:
1. 使⽤math模块中的⼀些函数,⽐如iling(天花板除法)。
2. python⾃带整除,python2中是/,3中是//,还有div函数。
3. 字符串格式化可以做截断使⽤,例如 “%.2f” %value(保留两位⼩数并变成字符串……如果还想⽤浮点数请披上float()的外⾐)。
4. 当然,对浮点数精度要求如果很⾼的话,请⽤decimal模块。
以下是对decimal模块的介绍:
decimal 模块:decimal意思为⼗进制,这个模块提供了⼗进制浮点运算⽀持
1.可以传递给Decimal整型或者字符串参数,但不能是浮点数据,因为浮点数据本⾝就不准确。
2.要从浮点数据转换为Decimal类型
from decimal import*
a = Decimal.from_float(12.222)
print(type(a), a)
运⾏结果:
<class'decimal.Decimal'>12.2219999999999995310417943983338773250579833984375
3.通过设定有效数字,限定结果样式
from decimal import*
getcontext().prec =6#保留六个有效数字
a = Decimal(1)/Decimal(7)
print(type(a), a)
运⾏结果:
<class'decimal.Decimal'>0.142857
4.四舍五⼊,保留⼏位⼩数
from decimal import*
a = Decimal('51.56501').quantize(Decimal('0.00'))
print(type(a), a)
运⾏结果:
<class'decimal.Decimal'>51.57
5.Decimal 结果转化为string
from decimal import*
a =str(Decimal('3.40').quantize(Decimal('0.0')))
print(type(a), a)
运⾏结果:
<class'str'>3.4
Python3中decimal处理计算精度问题⽰例:
#!/usr/bin/python3.6.6
# coding:utf-8
import decimal
from decimal import Decimal, getcontext
def demo():
"""
取整问题:
ROUND_CEILING 总是趋向⽆穷⼤向上取整
  ROUND_DOWN 总是趋向0取整
  ROUND_FLOOR 总是趋向负⽆穷⼤向下取整
  ROUND_HALF_DOWN 如果最后⼀个有效数字⼤于或等于5则朝0反⽅向取整;否则,趋向0取整
  ROUND_HALF_EVEN 类似于ROUND_HALF_DOWN,不过,如果最后⼀个有效数字值为5,则会检查前⼀位。
偶数值会导致结果向下取整,奇数值导致结果向上取整
  ROUND_HALF_UP 类似于ROUND_HALF_DOWN,不过如果最后⼀位有效数字为5,值会朝0的反⽅向取整
  ROUND_UP 朝0的反⽅向取整
  ROUND_05UP 如果最后⼀位是0或5,则朝0的反⽅向取整;否则向0取整
"""
# 1.常规计算
getcontext().prec =9
r1 = Decimal(1)/ Decimal(3)
print("r1 ", r1)# r1 0.333333333
# 2.但是getcontext().prec会包含⼩数点前⾯的所有长度,当前⾯长度有变化时并不能固定控制⼩数点后的位数
r2 = Decimal(10)/ Decimal(3)
print("r2 ", r2)# r2 3.33333333
# 3.想要固定控制⼩数点后⾯的位数则需要使⽤decimal.quantize(Decimal('0.00000000')),注意不能超过getcontext().prec的位数  r3 = Decimal(1)/ Decimal(3)
print("r3 ", r3.quantize(Decimal('0.00000000')))# r3 0.33333333
r4 = Decimal(10)/ Decimal(3)
print("r4 ", r4.quantize(Decimal('0.00000000')))# r4 3.33333333
r5 = Decimal(10)/ Decimal(str(1.5))
print("r5 ", r5.quantize(Decimal('0.00000000')))# r5 6.66666667
# 4.向上取整
getcontext().rounding =getattr(decimal,'ROUND_CEILING')# 总是趋向⽆穷⼤向上取整
r6 = Decimal(10)/ Decimal(str(1.5))# r6 6.66666667
print("r6 ", r6.quantize(Decimal('0.00000000')))
r7 = Decimal(10)/ Decimal(3)# r7 3.33333334
print("r7 ", r7.quantize(Decimal('0.00000000')))
# 5.向下取整
getcontext().rounding =getattr(decimal,'ROUND_FLOOR')# 总是趋向⽆穷⼤向下取整
r8 = Decimal(10)/ Decimal(str(1.5))# r8 6.66666666
print("r8 ", r8.quantize(Decimal('0.00000000')))
r9 = Decimal(10)/ Decimal(3)# r9 3.33333333
print("r9 ", r9.quantize(Decimal('0.00000000')))
if __name__ =='__main__':
demo()
运⾏结果:
r1  0.333333333
r2  3.33333333
r3  0.33333333
r4  3.33333333
r5  6.66666667
r6  6.66666667
r7  3.33333334
r8  6.66666666
r9  3.33333333

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。