首先,为啥会要讨论这个问题。

我得为昨天拖了小组后腿深表歉意。其实程序逻辑很快就理通了的,但自己总是会因为各种各样的小问题束缚手脚,看接下来这个图片:

稍微有数据敏感性的同学就能看出,中间这么一大堆又是0000又是999还是这么多位的小数,一看就是异常数据。这块数据的产生,源于代码里对两个字符串做了float转换并相减,导致出现了这种数据异常的错误。那么问题来了,1.这种异常是如何产生的?2.有哪些方法可以解决这种问题呢?3.编程中间还有哪些与这个问题相关的注意事项呢?

第一部分:这种异常是如何产生的呢?

我们先来看演示:

看来,直接输出float型数据,以及对字符串进行的float转换,本身并没有什么问题,那么为什么浮点数相减就会出现这个可恶的小尾巴呢?我们有必要从计算机本身数字加减的机制进行探究。有学习过《计算机组成原理》等基本课程、哪怕只是简单了解计算机内部运行机制的同学都明白,计算机内部的加减乘除都是要把数字转化成为二进制实现的。那么,我们此处的浮点数,也要转换为二进制,才能进行计算。Python内浮点数是用机器上浮点数的本机双精度(64 bit)表示的。提供大约17位的精度和范围从-308到308的指数。和C语言里面的double类型相同【可参考C语言double类型的解释】。

我们来看一个简单的例子。十进制1.1转换成二进制是什么数?十进制整数部分转化成二进制,用短除法处以2倒序取余。小数部分转化为二进制是用乘法乘2正序取整。见下面一个浮点数转二进制数的例子。

1.10整数部分就是1,转换成二进制1(这里整数转二进制不再赘述)
小数部分:0.1
0.1*2=0.2取整数部分0,基数=0.2
0.2*2=0.4取整数部分0,基数=0.4
0.4*2=0.8取整数部分0,基数=0.8
0.8*2=1.6取整数部分1,基数=1.6-1=0.6
0.6*2=1.2取整数部分1,基数=1.2-1=0.2
0.2*2=0.4取整数部分0,基数=0.4
.
.
.
直至基数为0。1.1用二进制表示为:1.000110...xxxx....(后面表示省略)

关于之前的演示,相当于,因为3.4的存储,发生了精度损失(3.5不会,因为3.5的二进制是11.1,补码存储依然不会发生精度损失),所以在相减的时候,发生了一次精度损失,最后结果存储的时候,再次发生一次精度损失。所以,才会出现最后的小尾巴情况。

第二部分:有哪些方法可以解决这个问题呢?

解决这个问题?不存在的,除非是提高精度——让计算机内能够完整的存储数字的二进制(二进制补码)表示,否则的话,只要有精度损失,就指不定什么时候会冒出来小尾巴。我们追求的解决,自然也是从提高精度,和“表面看起来正确”这两条道路去追求。

提高精度——Python本身自带的float已经是可支持浮点数的最高精度形式。当然,这个肯定是不能阻挡我们对更高精度的要求,这里可以自己实现高精度的数据形式,也可以使用Python扩展模块:Decimal。使用Decimal本身需要导入decimal包,初始化decimal数据可以使用整型数据和字符串,而不能使用float型数据,正如之前我们所说的那样,某些浮点数存储会发生精度损失——这意味着float本身就不够精确。

当然,还有很多抖机灵的方法,比如说结果转换成字符串然后再截取?!

你可能体会不到,这个是一种针对数据波动范围相对确定,相当实用的方法——虽然应该没有任何一个脑子正常的程序员会推崇这种方法。这种方法就是追求的“表面上看起来正确”,你看,最后的显示出来的结果不就是-0.1么?

自然,还有print的%精度控制,这里就不赘述。而且也不想详述这个,毕竟这个惊为天人的字符串截取方法,都还是对字符串进行了处理,而%精度控制只是显示的时候做了处理,可真是够“表面”的。

不得不说,也是受这种方法的启发,本人使用的方法,是利用Python int转换“舍去小数点后所有数字”的特点,把原浮点数乘以需要保留精度的位数,然后转换成整数,再除回去,这样就形成了“表面正确”的数据,效果不要太好。

总结一下!解决这个精度损失带来的“恶魔小尾巴”问题,我们大体上有提高数据格式精度和只追求最终显示改变两大思路。

提高数据格式精度:使用扩展包decimal

只追求最终显示改变:printf %精度控制 ,字符串截取指定位数,先移动小数点、转换成整型舍去末尾、再把小数点移动回来。等方法。

第三部分:编程中间还有哪些和这个问题相关的注意事项呢?

这个小尾巴让我可谓一开始是焦头烂额,也严重耽误了小组研究进度。通过我们之前的探究,可以发现,浮点数本身表示由于受计算机限制,经常是不精确的。所以,日常数据中,最好不要用浮点数。

可能有些人觉得精度损失一些没有什么,然而浮点数的精度损失关键时候可不只只是精度损失,甚至会影响流程控制!浮点数不只有Python里面有,咱们用更加基本的C++来说明这个问题:

当精度损失已经让程序的走向开始不符合逻辑的时候,你还会轻视这个问题么?

这里给广大同仁们分享一篇专门讲解浮点数的文章,深入了解,真的有很多可圈可点之处!

深入了解计算机浮点数机制——程序员必备!

【小思考】Python的float转换精度损失所想到的的更多相关文章

  1. Python之☞float浮点数精度问题

    Python的浮点数损失精度问题(转) 一个简单的面试题: >>>0.1+0.1+0.1 0.2 >>>0.1+0.1+0.1 0.3000000000000000 ...

  2. java学习笔记(3)数据类型、源码、反码、补码、精度损失、基本数据类型互相转换

    关于java中的数据类型: 1.数据类型的作用是什么? 程序当中有很多数据,每一个数据都是有相关类型的,不同数据类型的数据占用的空间大小不同. 数据类型的作用是指导java虚拟机(JVM)在运行程序的 ...

  3. WebGL着色器32位浮点数精度损失问题

    问题 WebGL浮点数精度最大的问题是就是因为js是64位精度的,js往着色器里面穿的时候只能是32位浮点数,有效数是8位,精度丢失比较严重. 这篇文章里讲了一些处理方式,但是视坐标这种方式放在我们的 ...

  4. Java中关于 BigDecimal 的一个导致double精度损失的"bug"

    背景 在博客 恶心的0.5四舍五入问题 一文中看到一个关于 0.5 不能正确的四舍五入的问题.主要说的是 double 转换到 BigDecimal 后,进行四舍五入得不到正确的结果: public ...

  5. Java double和 float丢失精度问题

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt357 由于对float或double 的使用不当,可能会出现精度丢失的问题. ...

  6. java float double精度为什么会丢失?浅谈java的浮点数精度问题 【转】

    由于对float或double 的使用不当,可能会出现精度丢失的问题.问题大概情况可以通过如下代码理解: public class FloatDoubleTest { public static vo ...

  7. double float的精度问题

    三部曲 1: #include <iostream> #include <stdio.h> #include <string.h> using namespace ...

  8. Java中的小数运算与精度损失

    float.double类型的问题 我们都知道,计算机是使用二进制存储数据的.而平常生活中,大多数情况下我们都是使用的十进制,因此计算机显示给我们看的内容大多数也是十进制的,这就使得很多时候数据需要在 ...

  9. JAVA浮点数计算精度损失底层原理与解决方案

    浮点数会有精度损失这个在上大学的时候就已经被告知,但是至今完全没有想明白其中的原由,老师讲的时候也是一笔带过的,自己也没有好好琢磨.终于在工作的时候碰到了,于是google了一番. 问题: 对两个do ...

随机推荐

  1. [php]mysql操作流程

    这种是比较老的一种mysql连接方法 1.连接数据库 $this->con = mysql_connect($this->host, $this->user, $this->p ...

  2. ETL 自动化测试框架

    分享个自己最近在做的自动化测试框架架构图. 数据的测试,入口一般定时任务.可添加参数选择执行任务的范围,也可以选择默认的执行范围.验证测试的为etl测试.数据库中的字段校验(通过对应关系.接口或者SQ ...

  3. cmmusic:小巧而实用的mplayer音乐播放前端

    Author: Jiqing (jiqingwu@gmail.com) home: http://hi.baidu.com/jiqing0925 create: 2011-03-10 update: ...

  4. Callable,Future,FutureTask

    1.概念定义 2.实现例子 3.总结   1.概念定义   1.Callable Callable是一个接口,效果类似Runnable接口.实现该接口,然后,耗时操作在call()方法中执行.与Run ...

  5. VC++的全局变量(转)

    全局变量一般这样定义:1.在一类的.cpp中定义 int myInt;然后再在要用到的地方的.cpp里extern int myInt:这样就可以用了. 2.在stdafx.cpp中加入:int my ...

  6. 20165230 《Java程序设计》实验四 Android程序设计实验报告

    20165230 <Java程序设计>实验四 Android程序设计实验报告 一.实验报告封面 课程:Java程序设计 班级:1652班 姓名:田坤烨 学号:20165230 成绩: 指导 ...

  7. 关于new Handler()与new Handler(Looper.getMainLooper())区别

    如果你不带参数的实例化:Handler handler=new Handler();那么这个会默认用当前线程的Looper对象. 一般而言,如果你的Handler是要用来刷新UI的,那么就需要在主线程 ...

  8. 畸形的 dockerfile中的COPY命令-

    dockerfile中的COPY是指COPY 指定目录的“子级目录”下所有的目录和文件,到指定目录中,这个shell中的cp命令大相径庭,使得很多人纳闷,怎么cpy过去的文件不是自己想要的

  9. bzoj 1607 Patting Heads 轻拍牛头

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1607 题解: 题目似乎出错,应为“同时拍打所有所持纸条上的数字能被此牛所持纸条上的数字整除 ...

  10. mysql命令补全工具

    需要在linux中下载mysql插件. 安装mysql插件 yum -y install epel-release python-pip python-devel pip install mycli ...