有这样一个问题:乘以3是多少?
答案毫无疑问是1。
但是你有没有想过这样一个问题:写成无限循环小数是0.
,用0.
乘以3,答案是0.
,对吗?
这两个答案似乎产生了矛盾。
答案是1,这是毫无疑问的,那么问题就出在0.乘以3上了。
0.333333...,这是一个无限循环小数,但对于无限循环小数,我们真的完全了解它吗,在它的省略号里面,到底是什么。这里,就要引入级数的概念了。
级数,简单来说,就是一组无限数列的和。
那么,对于0.,我们可以将其写成级数的形式:
定义这样一个数列{a}:
a=0.3
a=0.03
a=0.003
......
a=
因此,0. =0.3+0.03+0.003+......+
=a
+a
+a
+......+ a
令S为{a}的n项和,由等比数列求和的公式,
,
,很神奇地,0.
经过运算,又变成了
所以,
似乎什么都没说,是吗,但至少我们知道了0.如何变回
,这就是意义所在。
那么在计算机中,这个问题又是怎样的呢,我立刻写了一个小儿科的代码
#include <stdio.h>
int main()
{
double a;
a = 1.0/3;
a *= 3;
printf("%.17f",a);
return 0;
}
输出结果是1.00000000000000000
嗯,没有问题。
那我们看一看a=1.0/3后a的值呢。
通过调试系统,看到a=0.33333333333333331
欸,奇怪,1.0/3怎么变成0.33333333333333331了呢,再不济也应该是0.33333333333333333啊。而且0.33333333333333331乘以3怎么也不会是1啊,这下就连级数都没法解释了。
你不能在0和1的世界里讨论0和1以外的事,我不懂。
想要弄明白这个问题,我们呢就要明白小数在计算机中是如何储存的。
计算机是采用定点数和浮点数的形式存储小数的,具体的不再赘述,结论是:定点数表示小数没有误差,但是非常占内存且麻烦。浮点数更加灵活,但带来的问题是表示不精确,与实际的值存在误差。
我选择用来表示小数的,就是浮点数(double)
我们知道,在实数范围内,实数是稠密的,也就是说,任意挑两个数出来,无论这两个数靠的有多近,总能在这两个数之间找到其他数。
而在浮点数表示法中,数不是稠密的,而是间断的,也就是说数与数之间是有空隙的。这样就造成了实数的稠密性与浮点数的不稠密性之间的矛盾。那么,怎样表示两个间断的浮点数之间的数呢。
继续采用一些傻瓜式的调试方法寻找答案...
#include <stdio.h>
int main()
{
double a;
a = 1.0000000000000001/3;
printf("%.17f",a);
return 0;
}
当分子为1.0000000000000001时,结果为0.33333333333333331;如果减少一个0,结果就差变成0.3333333333333337了,所以我们在前者的基础上改变末尾的数字:
#include <stdio.h>
int main()
{
double a;
a = 1.0000000000000002/3;
printf("%.17f",a);
return 0;
}
结果为0.33333333333333343
好,那么我们在1.0000000000000001的后面加数字:
1.00000000000000011/3,结果还是0.33333333333333331
1.00000000000000012/3,结果变成0.33333333333333343
到现在其实已经很清楚了, 0. 附近的两个浮点数是0.33333333333333331和0.33333333333333343,按照就近原则,取的是更近的0.33333333333333331。
为了验证这一想法,又写了一个:
#include <stdio.h>
int main()
{
double a = 0.33333333333333333333333;
printf("%.17f",a);
return 0;
}
结果为0.33333333333333331。也就是说,无论你是真正的0 .,是一个无限循环小数 ,还是只是一个有很多个3的有限小数,在计算机内都会按照浮点数进行存储。验证完毕。
那么至于浮点数的乘法,我就不清楚了为什么会有进位的情况发生呢。
这里再放一个伪级数(因为计算机中不能真正取到无穷,只能用0x3f3f3f替代)
#include <stdio.h>
#define inf 0x3f3f3f
int main()
{
int n=1;
double i;
double sum = 0;
for(n; n<inf ;n++)
{
i = 3.0/pow(10,n);
sum += i;
}
printf("%.17f %.17f",sum,3*sum);
return 0;
}
结果为0.33333333333333326 0.99999999999999978
大概猜测一下,浮点数做乘法时是往后取的。
好啦,这篇文章就写到这里啦,只是为了自己搞着玩,去验证一下自己的猜想而已,大家就当看一乐吧。