关于这个笔记
也许你已经学会了一门计算机语言,也许你会写一段不错的程序。
也许你在嘈杂的环境用不费力的就可以用熟悉的IDE完成一个项目。
不过,你还在用+ - * /和%做所有的计算么?
这个笔记就是为了写出更加正确的程序,小小的一个学习过程。
我不总结复杂的道理,只记录用得上的方案。
出现的一切代码都使用Java语言。
正确的计算过程导致错误的计算结果
关于基本数值计算中的一些陷阱
浮点数和实数
当早期的Fortran和algol创造出real这个关键字时,你还真以为那么是实数,不过说真的,你被骗了。
所以到了C,我们拥有了float。
那么他们到底有什么不同呢?
简单地说
System.out.println(”Value of 1/3: ” + (1/3f))
将输出
Value of 1/3: 0.33333334
很简单,这是很早之前您就知道的四舍五入。当时我们只是那无法写完的循环在纸上划下一个”≈”,然后非常开心可以少写几个字符。如果你有一个负责任的数学老师,他(她)就会反复的叮咛你,要小心,计算时候要保留结果精度往后一位的数值。
当这一切轮到了计算机,我们叫它:
舍入误差
舍入误差有时相当惊人。对1/3连加840次,你会得到279.9915(本应当为280),如何惊人,我们会在下面看到。
不过有意思的事情,和小时候的情形不一样,舍入误差并不是由于不能除尽或者是无理数造成的,类似于1/5的数字连加之后也有误差。这是因为绝大多数
计算机是基于2进制体系的,所以IBM有用于商业计算的10进制计算机。简单的事实是利用float表达的2的幂次,在精度范围是没有舍入误差的。
给出常用定义:
作如下计算,你会期待怎样的结果呢?
别偷懒,用你久远的口算能力,很容易就可以得出,它本来应该是0。而事实上,我们伟大的计算机的答案是3222784.0
为什么?
因为,10000001/20000000的时候,答案本应该是0.50000005,由于舍入误差,我们得到了0.5000006。
接下来我们减去0.5的时候,得到的应当是0.00000006,这样,我们就丢失了所有的正确的数。
更糟糕的是,计算机中实际得到的值是5.9604645e-8。
因此,您的存款户头上可能会平白无故减少3222784元。只因为银行决定向你支付利息之后,调整了汇率。
呃,这一次,我们叫它:
相消误差——两个非常相近的值相减时,如果消去了绝大多数有效数值,那么就会发生相消误差。
舍入误差——是精确值和其可表达值之间的差值。
如果还有什么要说的,那就是:
Double比float 更接近实数,貌似不需要我说。
浮点运算不遵守代数定理
比如:a = 1.0, b = 3.0e-8, c = 4.0e-8
那么: (a+b)+c = 1.0
a+(b+c) = 1.0000001
不过加法和乘法还是满足交换律的。
下一次:轮到了整数。