之前对浮点数运行没有没有太在乎。昨天同事要求把百分比结果保存到文件上,而后就以保存1位小数的形式给他保存了。html
可是今天同事运行时问能不能统一以一位小数的形式保存,当时以为很奇怪昨天就是以一位小数形式存的怎么还会提这种要求呢。python
其给回的截图确实是部分是一位小数的,但一部分是很长的。查看代码都统一以下格式:编码
# 使用round保留三位小数,而后乘以100,最后格式化为带百分号的字符串 rate=f"{round(x/y,3) * 100}%"
代码上没看出什么问题,直接运行确实是有些结果是一长串的。进行调试发现当x为37y为76时即会出现问题,以下图所示:spa
进行步骤拆分,发现round方法没有问题,问题在浮点数乘以100上(同时以下图能够看到也不是全部浮点数乘都有问题)3d
搞不清缘由,直到看到这篇文章:https://www.programiz.com/python-programming/numbers调试
大意是说二进制对不少浮点数没法准确表示只能用一个近似值代替,而当使用这些以近似值代替的浮点数进行进算时本质上是这些进似值参与了运算,出来的结果也就是进似值运算后的结果。code
也就是说,一是这不是乘100的问题也不是乘法的问题而是整个浮点数运算都有问题,二是这不是python的问题是计算机浮点数存储的问题像C、Java等其余计算机语言进行运算都会有问题。htm
可能有人会疑惑:为何二进制能够表示2不能表示0.2呢?blog
这是由于数值和字符串是不同的,若是是字符串那么表示2.2点的左右两边的2编码是同样的就能够了(如ASCII码:504650),但数值不是这样,数值的整数部分和小数部分须要一个统一的表示形式,那就是加权位计数法。ci
整数部分都要以2的0次方(20)到2的无穷次方(2∞)表示,这没有问题,只要长度足够就能表示出全部奇数和偶数。2 = 1 * 21 + 0 * 20 = 10
小数部分都要以2的-1次方(2-1)到2的负无穷次方(2-∞)表示,这就有问题,由于好比2-1...2-∞无论怎么组合都不能彻底等于0.2。0.2 = 0 * 2-1 + 0 * 2-2 + 1 * 2-3 ...
这状况让我想起上份工做局方领导的一句话,应该是“能够理解但不能接受”。
原理上二进制没法精确表示一些浮点数能够理解,可是就这么返回个显然错误的结果给用户那是没法接受的。
python提供了Decimal()方法让浮点运算结果能够和人平时运算的结果同样。(Decimal本质应该仍是经过加长长度提升精度)
# Decimal传字符串才能准确表示,因此须要先用str()把round()的结果转为字符串 rate=f"{Decimal(str(round(x/y,3))) * 100}%" # 其实上边的结果出来是48.700%的形式,即三位小数的形式并不太符合咱们保留一位小数的想法,真正符合想法得下面这样 # rate=f"{round(Decimal(str(round(x/y,3))) * 100, 1)}%" # 其实咱们说了这么多,咱们都是创建在决定保留多少位再乘100这个前提下,假若咱们先乘100后决定保留几位那都不须要用Decimal # rate=f"{round(x/y*100,1)}%"
参考:
https://www.programiz.com/python-programming/numbers