当前位置: 首页 > 后端技术 > Python

Python中浮点数四舍五入的问题

时间:2023-03-25 22:47:39 Python

昨天遇到了一个问题。当6.6045保留三位小数时,我们使用round()函数进行计算。我们希望得到6.605,但是:>>>round(6.6045,3)6.604网上有人说是因为在计算机中,小数点是不准确的。比如1.115在计算机中实际是1.1149999999999999991182,所以当你把这个小数精确到小数点后两位时,小数点后第三位实际上是4。所以四舍五入就是1.11。这个说法对了一半。因为在计算机中并不是所有的小数都是不精确的。比如0.125的小数,在电脑里是准确的,是0.125,后面的数值没有省略,没有近似值,确实是0.125。但是如果我们在Python中运行:>>>round(0.125,2)0.12whyAreyouhere?还有更奇怪的是,又一个可以在计算机中精确表示的小数0.375,我们看看精确到小数点后两位:>>>round(0.375,2)0.38怎么又输入到这里了?解析是因为在Python3中,round为了小数点的精度,采用四舍五入到50%的方式。如果你写过大学物理实验报告,那你应该记得老师说如果直接用四舍五入,最后的结果可能会偏高,所以需要用奇数四舍五入和偶数四舍五入。比如对于一个浮点数a.bcd,需要精确到小数点后两位,那么就要看第三位小数了:如果d小于5,直接舍弃;如果d大于5,直接进位;如果d等于5:d如果后面没有数据,c为偶数,则没有进位,cd后面没有数据,c为奇数,则进位,c变为(c+1)如果d后面有非零数,比如实际小数是一个.bcdef,这时候必须四舍五入,c就变成了(c+1)关于奇偶四舍五入,有兴趣的朋友可以搜索这些维基百科中的两个条目:数值舍入和奇偶舍入。所以,如果round给出的结果和预期的不一样,那么你需要考虑两个原因:你的小数能不能准确地存入电脑?如果不能,那么它可能没有四舍五入到最接近的一半,例如1.115,它的小数点后第三位实际上是4,当然会四舍五入。如果你的小数可以在计算机中准确表示,那么round使用的舍入机制是奇偶舍入,所以要看你要保留的位是奇数还是偶数,它的下一位有没有数据在后面。关于奇偶宫,有兴趣的朋友可以搜索这两个名词:数值四舍五入和奇偶宫。回到原题,对于浮点数6.6045,我们在Scheme中查看它的确切形式:>(exact6.6045)3718002967371055/562949953421312也就是说不能准确存储,大概是6.60449999999999的形式…,所以当四舍五入时你得到6.604。如何正确进行四舍五入如果要实现数学四舍五入,就需要用到decimal模块。具体用法参考官方文档:https://docs.python.org/zh-cn....其中quantize的函数原型和文档中提到可以通过指定舍入参数来确定舍入方式.如果没有指定舍入参数,则默认使用上下文提供的舍入方法。现在看看默认上下文中的舍入方式是什么:>>>fromdecimalimportgetcontext>>>getcontext().rounding'ROUND_HALF_EVEN'ROUND_HALF_EVEN其实就是奇数舍入和偶数舍入。如果要指定真正的舍入,那么我们需要在quantize中指定舍入方式为ROUND_HALF_UP:>>>fromdecimalimportDecimal,ROUND_HALF_UP>>>Decimal('0.125').quantize(Decimal('0.00'),rounding=ROUND_HALF_UP)Decimal('0.13')现在一切看起来都很好。可能有人会进一步问,如果Decimal接收的参数不是字符串,而是浮点数怎么办?我们来实验一下:>>>Decimal(0.125)Decimal('0.125')那是不是说Decimal的第一个参数可以直接传浮点数呢?我们换一个数字来测试一下:>>>Decimal(11.245)Decimal('11.24499999999999992184029906638897955417633056640625')传入浮点数11.245和字符串'11.245'后的结果其实是不一样的。我们继续在文档中寻找答案:官方文档已经明确说明,如果你传入的参数是浮点数,而这个浮点值在计算机中无法准确存储,会先进行转换为不准确的二进制值,然后将此不精确的二进制值转换为等效的十进制值。对于不能准确表示的小数,当你传入的时候,在Python获取到数字之前,这个数字已经被转换成了一个不准确的数字。所以虽然传入的参数是11.245,但是Python实际得到的是11.24499999999...免费下载试用:https://support.i-search.com.cn/