[ 转自 果壳 原著matrix67 ]Android计算器低级错误?都是二进制惹的祸!

Android 计算器惊现超级大 bug!在 Android 的计算器程序里输入 14.52 - 14.49,计算器竟然说它等于 0.0299999999!其实,这已经是计算机的老毛病了。计算机用二进制来表示数,将会不可避免地产生误差。

听说了 Android 的超级大 bug,我立即在自己的 HTC Hero 上试了一下,果然正如众人所说, 14.52 - 14.49 = 0.0299999999。稍作试验便可发现,一些更为简单的算式也会出现类似的问题,例如在 Android 计算器中输入 1.2 - 1.1,结果等于 0.0999999999。这是为什么呢?

都是二进制惹的祸

原来,在计算机内部,数字并不是用十进制来储存的,所有数字都是以二进制的方式储存的。但一个进制下的有限小数,很可能是另一个进制下的无限小数。比方说,把十进制小数 1.2 转换成二进制小数,将会得到一个无限循环小数 1.001100110011…;把 1.1 转换成二进制小数则是 1.0001100110011…,也是一个无限循环小数。计算机显然不能储存无穷多位数,因而不得不近似地截取有限多位。如果保留 52 位数的话,那么在计算机看来,1.2 - 1.1 其实是这样:

问题出现了:在显示计算结果的时候,计算机需要把它转换回十进制。但上面的结果转换成十进制并不是精确的 0.1,而是一个 52 位小数 0.09999999999999986677323704 49812151491641998291015625。由于 2 的 -52 次方约为 10 的 -16 次方,也就是说 52 位二进制小数的精度大约相当于 16 位十进制小数,因此计算机上通常只保留这个小数的 16 位有效数字。因此,上面这个小数也就成了 0.09999999999999987。

不只是 Android 的问题

你会发现,这样的问题在电脑里到处都有。例如,在你的浏览器地址栏里输入

javascript:alert(1.2-1.1)

你就会看见这个诡异的答案:0.09999999999999987。即使是专业的数学软件中,小数精度的问题也是不可避免的。在 Mathematica 中输入 1.2 - 1.1,答案似乎是没错:

但是,输入 InputForm[%] 查看这个 0.1 的真身,你会发现原来它也不是精确的:

也就是说,Mathematica 发现这个小数太接近 0.1,便猜测这个数真的就是 0.1,于是智能地帮我们化简了。很多计算器类的软件也有自动化简的功能,只是 Android 上的计算器做得似乎还不够好,偶尔会出现失误罢了。

其实,这个问题还不算离谱。在我的 Android 手机上输入 1.2 - 1.1 - 0.1,会得到一个更加诡异的结果:-1.38777E-16,也就是 -1.38777 × 10 -16 。这也是由于二进制表达的误差造成的。不过,较新的 Android 系统上似乎没有出现这个问题,可见 Android 处理小数的算法也是在不断改进的。

时间: 2024-08-02 19:24:49

[ 转自 果壳 原著matrix67 ]Android计算器低级错误?都是二进制惹的祸!的相关文章

Android计算器界面布局

Android计算器界面图: 所定义的XML布局文件,主要用到的是TableLayout: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_heigh

Android计算器尝试

学了一段时间Android了,一直都是在看,没有什么尝试,刚好最近大致学会了gridview配合simpleadpter的使用,于是想着动手练习一下,就选择了写一个最简单的计算器来实现. 只包含+-*/四种最基本的运算,不过即使是这么简单的小程序也搞了大半天才搞定.也是因为第一次写的时候比较随意,没有画流程图,方法也封装的好,结果后来优化调试的时候特别难受,最后一怒之下删除重新来过,这次注重了整体流程的把握和方法的封装写起来思路清晰了很多,不一会就调试实现了预期的功能,可以说是一次不错的尝试吧!

在C\C++编程时常范的低级错误总结

大学毕业快两年,也当快两年的码农,最近在总结下自己在两年中编程时常范的低级错误. 1.宏里面有return语句 如: #define ACE_NEW_RETURN(POINTER,CONSTRUCTOR,RFT_VAL) \ Do{ \ POINTER = new CONSTRUCTOR; \ If(POINTER  ==NULL) \ { \ Return RFT_VAL;\ } \ } 当执行如下语句时: ACE_NEW_RETURN(g_Proctimer,CONSTRUCTOR,RFT_

Error:Execution failed for task &#39;:app:compileDebugAidl&#39;. &gt; aidl is missing(Android Studio编译错误)

今天下载了一个1.2.1.1版本的Android Studio,使用VPN更新好SDK之后就新建了一个hello world工程测试一下环境,然后就出现以下错误: Error:Execution failed for task ':app:compileDebugAidl'. > aidl is missing 什么情况?全新安装的环境也有错误?Google发布这个工具时没有测试好吗? 没搞过这个工具,一阵搜索得到以下方案: 1.右键工程->Open Module Settings 2.将Bu

[转]编译Android源代码常见错误解决办法

1. 编译时出现/usr/bin/ld: skipping incompatible /usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../libz.so when searching for -lz PLATFORM_VERSION_CODENAME=AOSPPLATFORM_VERSION=AOSPTARGET_PRODUCT=genericTARGET_BUILD_VARIANT=engTARGET_SIMULATOR=TARGET_BUILD_TYPE

Android 开发之错误整理java.lang.SecurityException: Requires READ_PHONE_STATE: Neither user 10088 nor current process has android.permission.READ_PHONE_STATE.

java.lang.SecurityException: Requires READ_PHONE_STATE: Neither user 10088 nor current process has android.permission.READ_PHONE_STATE. 今天写了一款发短信的软件,拿了个酷派5879,试了下,结果不能用,把try{}catch{}去掉了,报这个错误, android.permission.READ_PHONE_STATE.没有READ_PHONE_STATE权限,

Android自动化压力测试之Monkey Test Android常见的错误类型及黑白名单的使用方法(四)

Android常见的错误类型有两种 1.ANR类型 1)在5秒内没有响应输入的事件(例如,按键按下,屏幕触摸) 2)BroadcastReceiver在10秒内没有执行完毕 2.Crash类型 1)异常停止 2)异常退出 Monkey 调试参数 --kill-process-after-error  犹豫一个错误而停止时,出错的应用程序将继续处于运行状态 --wait-dbg   启动monkey后,先中断其运行,等待调试器附加上来 命令演示 adb shell monkey -p com.da

关于实现数据查询条件输入功能的一个低级错误

我们常常要实现检索数据的功能.复杂的查询条件输入,最好有辅助输入功能,能帮助使用者更轻松的完成查询条件输入.最近我们见到一个查询条件输入功能实现的时候犯的一个低级错误,觉得在新手中可能会典型,故拿出来说一下. 有个查询基站监控历史数据的功能,要查基站的历史数据,先得选择几个基站.第一个版本查询条件很简单,只需按站名或地区搜索基站,在结果集中选中一个或多个站,再输入其他查询条件.第二个版本,客户要求增加基站的基础信息作为查询条件,比如郊区还是市区,墙体材料等等,这些条件影响基站的冷却所需的能耗.这

Android 笔记之错误记录

前言--好记性不如烂笔头,记录Android学习过程中遇到的各种问题BUG.O(∩_∩)O 错误1 -- Caused by: android.content.res.Resources$NotFoundException: String resource ID #0x0 错误原因--一般是int 型数据赋给String ,然后让TextView显示就会出现如上错误. 解决办法--用String.valueOf 或者在int数据后加"" 错误2-- 在布局文件中,文本的设置使用如下写法