对非正确使用浮点型数据而导致项目BUG的问题探讨
来源:互联网 发布:湖南大数据公司有哪些 编辑:程序博客网 时间:2024/05/21 08:42
乘法分配律
在上小学的时候就已经学习过乘法分配律,乘法分配律的具体内容是:两个数的和与一个数相乘,可以先把他们分别与这个数相乘,再相加,得数不变。乘法分配律的定义还可以用表达式“(a+b)×c = a×c+b×c”的形式给出。乘法分配律的反用“a×c+b×c = (a+b)×c”同样成立。例如“10.2×(3+7) = 10.2×3+10.2×7 = 102”(反用形式为“10.2×3+10.2×7 = 10.2×(3+7) = 102”)。
计算机世界中的乘法分配律
为了一窥计算机世界中的乘法分配律,本文给出以下实例进行探究。
本例中先将整数24(纪念儿时玩过的名为“24点”的游戏)分割成四个整数的和,此部分操作在GetNumberList()方法中实现,分割后的四个整数保存进结构体变量中,整数24的所有分割结果则保存进List<Number>类型的集合numList中。接着遍历集合numList,每次遍历中先取出24的四个分割数分别与5相乘再相加(相当于乘法分配律中的a×c+b×c部分)得到结果result1,再将计算的结果result1与24 * 5(相当于乘法分配律中的(a+b)×c部分)的结果result进行比较,相等则输出true,否则输出false。
using System;using System.Collections.Generic;namespace NoticeDetailExp1{ class Program { static void Main(string[] args) { List<Number> numList = new List<Number>(); numList = GetNumberList(); int result = 24 * 5; int result1; foreach (Number n in numList) { result1 = n.Num1 * 5 + n.Num2 * 5 + n.Num3 * 5 + n.Num4 * 5; } Console.WriteLine("n.Num1={0}, n.Num1={1}, n.Num1={2}, n.Num1={3}:result1 == result? {4}", n.Num1, n.Num2, n.Num3, n.Num4, result1 == result); } private static List<Number> GetNumberList() { List<Number> numList = new List<Number>(); for(int i=0; i<=24; i++) for(int j=0; j<=24; j++) for(int k=0; k<=24; k++) for(int l=0; l<=24; l++) if (i + j + k + l == 24) { Number num = new Number(); num.Num1 = i; num.Num2 = j; num.Num3 = k; num.Num4 = l; numList.Add(num); } return numList; } } public struct Number { public int Num1 { get; set; } public int Num2 { get; set; } public int Num3 { get; set; } public int Num4 { get; set; } }}
运行程序,得到下图所示结果。
大家从图中能看出什么呢?
【DD(不是“小弟弟”的缩写,至于是什么,你猜):我从图中可以看出,显示结果不全。
作者:呵呵,DD真是观察细致入微。结果是不全,但是本人通过拖动滚动条已经确认所有返回结果都是true】
从结果了解到对于整型数据来说,乘法分配律完全适用。
那么将例子中的整型数据换成Double型数据,结果又会怎样呢?
修改实例中的部分代码,其他代码保持不变,下面仅给出修改部分的代码。
//将整数5修改成Double型数183.70833333333334//将result,result1的类型由int修改成doubledouble result = 24 * 183.70833333333334;double result1;foreach (Number n in numList){ //将整数5修改成Double型数183.70833333333334 result1 = n.Num1 * 183.70833333333334 + n.Num2 * 183.70833333333334 + n.Num3 * 183.70833333333334 + n.Num4 * 183.70833333333334; Console.WriteLine("n.Num1={0}, n.Num1={1}, n.Num1={2}, n.Num1={3}:result1 == result? {4}", n.Num1, n.Num2, n.Num3, n.Num4, result1 == result);}执行修改后的代码,得到下图所示的结果。当然,这里给出的结果也是不全的,不过对说明问题没有任何影响。
从图中可以看出,将整型数据换成Double型数据后,返回的结果中出现了相当数量的false,也就是说result与result1不再是绝对地相等了。我们可以通过Debug程序看看其中到底发生了什么。 下面以图中显示的返回结果为false的第一条数据为例。看看此时的result1的值是多少,为4409.0000000000009,而result的值为4409.0,很显然将两者进行相等比较,自然会返回false,虽然两者仅相差0.0000000000009,不相等就是不相等。
那么,0.0000000000009的差距是怎样产生的呢。
我们知道计算机在计算表达式“result1 = n.Num1 * 183.70833333333334 + n.Num2 * 183.70833333333334 + n.Num3 * 183.70833333333334 + n.Num4 * 183.70833333333334;”的值时,其实会将运算分解成多步,用代码来表示其运算过程的话,应该与以下代码所示的运算过程类似。
double multi1 = n.Num1 * 183.70833333333334;double multi2 = n.Num2 * 183.70833333333334;double multi3 = n.Num3 * 183.70833333333334;double multi4 = n.Num4 * 183.70833333333334;result1 = multi1 + multi2 + multi3 + multi4;
而我们知道,浮点运算是不准确的,因为:计算机在处理浮点数的时候,会先把浮点数(float , double)转换成整数再转换成二进制,然后进行操作,如果有取余,会有不同的取余方式。 再加上运算完成后,将二进制转换成上层的浮点数时,又会有一些取舍。这样一来,就会使最终的计算结果存在一定的误差。
再回到我们的问题,计算“double result = 24 * 183.70833333333334”会进行一次浮点运算,而计算“result1 = n.Num1 * 183.70833333333334+ n.Num2 * 183.70833333333334+ n.Num3 * 183.70833333333334+ n.Num4 * 183.70833333333334”时至少会进行5次浮点运算或者更多(这里有点拿不准),每一次浮点运算都有可能伴随着误差的发生,所以导致最终看到的结果会随机性地产生一些偏差。所以,在计算机的世界中,乘法分配律将不再适用,当然如果你对这一点点的损失无动于衷的话,你也可以认为在计算机世界中乘法分配律相对地适应于浮点运算。
不过,这个问题隐藏的也太深了点吧。
非正确使用浮点型数据导致项目BUG了
在开发过程中要是没有考虑到前文所述的问题,往往会导致一些奇葩的BUG。
曾经在项目代码中就看到了与实例代码相差无几的一段代码,代码中同样使用if (result1 == result)来进行条件判断,满足此条件后再进行其他一些操作,项目上线后很长一段时间都相安无事,突然有一天这段代码产生BUG了,至于BUG原因,就算我不说大家也清楚。
后来花了很大力气才搞清楚了问题的所在,问题弄清楚后,也才有了本文的产生。
所以本文作者想告诉大家:不管怎么说,开发中,在使用浮点型数据时,我们必须清楚浮点运算的特点,以免产生本文所述的类似的问题。
- 对非正确使用浮点型数据而导致项目BUG的问题探讨
- 【C++】使用局部变量赋值而非引用,导致内存多次释放的野指针问题
- awk 没有正确使用换行符导致的一个 BUG
- 数据完整性(数据的似真性而非正确或者错误)
- js对浮点数计算的bug
- android studio使用LBS API而未正确填写SHA1导致的错误
- 关于使用PyTorch设置多线程(threads)进行数据读取而导致GPU显存始终不释放的问题
- 经验问题导致的bug
- js处理浮点型的bug问题--js精度丢失
- [Bug]ArcSDE10/10.1删除非版本数据慢的问题
- 对安装QQ高版本导致键盘锁定问题的探讨
- 浮点型数据的精度控制问题
- actionForm对空表单项目,返回“”而非null的意外
- gnu-c 对浮点型数据除0.0的处理
- 针对使用python psutil库来kill进程而导致任务栏,小图标仍然显示的bug
- mybatis对非空的判断Bug
- 你的大数据项目使用的工具正确吗?
- 解决返回数据中存在Null而导致的错误问题
- codeforce 6A
- 15
- POJ 2299 - Ultra-QuickSort
- 有意思的游戏:Google XSS Game
- 16
- 对非正确使用浮点型数据而导致项目BUG的问题探讨
- linux编程合并多个静态库.a为一个.a
- DATAGUARD 估算业务高峰redo传送网络的带宽
- JQuery日期插件datepicker的使用
- 17
- HDU1102_Constructing Roads(最小生成树)
- ubuntu 14.04 安装chrome及给chromium 安装 flash player
- Bootloader - Main system - Recovery的三角关系
- codechef : Marbles 题解