再聊python的round

再聊python的round,第1张

大家都知道python3版本的round跟我们平时想象的不一致,在有的时候又需要用传统的round,比如在股票市场计算的时候,涨停价就是在昨收基础上乘以1.1或者1.2,然后round,这个时候的round需要使用传统意义上的四舍五入,否则价格会有差异。

至于python3版本的坑,这里不多说。有的人说会向偶数靠近,有的人说向奇数靠近,其实都是错误的,可以看看下面的例子:

print(round(3.1350001, 2)) # 结果3.14 进位,符合预期
print(round(3.135, 2)) # 结果3.13 不符合预期,谁说的向偶数靠近?
print(round(3.445, 2)) # 结果3.44 不符合预期,谁说的向奇数靠近?
print(round(3.145, 2)) # 结果3.15 进位,符合预期,3.445和3.145得到不同的预期?
print(round(3.535, 2)) # 结果3.54 进位,符合预期,3.135和3.535得到不同的预期?

网上也有说自己加个0.5来取整的算法,伪代码如下(不考虑负数的圆整):

def round(number, ndigits):
    return int((number+5*10**(-ndigits-1))*10**ndigits)/10**ndigits

看着好像是对的,但是我们把代码展开后,用3.135来测试一下

def round(number, ndigits):
    #number=3.135
    #ndigits=2
    r = 5*10**(-ndigits-1)  # r=0.005
    a = number+r            # a=3.1399999999999997 就是这里有猫腻,并不是想象的3.14
    b = a*10**ndigits       # b=313.99999999999994
    c = int(b)              # c=313
    d = c/10**ndigits       # d=3.13
    return d

其实问题就是浮点计算时,二进制浮点数被截断了,所以导致出现 3.1399999999999997 这样的数据,而不是3.14

所以,综上,网上好多的自定义算法都有问题。

下面给出正确的方法:

def old_round(number, ndigits): # 耗时大概是round的2倍多点
    arr = str(number).split('.')
    if len(arr) > 1 and len(arr[-1]) > ndigits and '5' == arr[-1][ndigits]:
        if number >= 0:
            return round(number + 10**(-ndigits-1), ndigits)
        else:
            return round(number - 10**(-ndigits-1), ndigits)
    else:
        return round(number, ndigits)

至于Decimal类型,我不想因为一个round而引入这玩意,上面的函数,测试上面的例程,结果均正确。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/789144.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-05
下一篇 2022-05-05

发表评论

登录后才能评论

评论列表(0条)

保存