关于这个笔记

也许你已经学会了一门计算机语言,也许你会写一段不错的程序。

也许你在嘈杂的环境用不费力的就可以用熟悉的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

不过加法和乘法还是满足交换律的。

下一次:轮到了整数。