今天,不对,准确说是昨天(不知不觉就凌晨了),又做了14届蓝桥杯b组第二题,猛一看,感觉蓝桥杯b组的题不是我的菜,太简单是一个求调和级数的问题,题如下:

1/1 + 1/2 + 1/3 + 1/4 + … 在数学上称为调和级数。
它是发散的,也就是说,只要加上足够多的项,就可以得到任意大的数字。
但是,它发散的很慢:
前1项和达到 1.0
前4项和才超过 2.0
前83项的和才超过 5.0
那么,请你计算一下,要加多少项,才能使得和达到或超过 15.0 呢?
请填写这个整数。
注意:只需要填写一个整数,不要填写任何多余的内容。比如说明文字。

正确代码如下:

#include <stdio.h>

const int MAXN = 1e7;

int main(int argc, const char * argv[])
{
    double sum = 0;
    int i = 1;
    for (; i < MAXN; i++)
    {
        sum = sum + 1.0 / i;
        if (sum >= 15.0)
        {
            printf("%d\n", i);
            break;
        }
    }

    return 0;
}

一看题感觉没啥含金量,于是随手就做了起来,可是做后一直无法正常运行,搞的我好为难,于是自习审查,发现我一开始将sum和i都定义成了int型,说到int型函数我就要说道说道了,曾经对int型只知道这是个整形函数,但是并没有从本质上研究过他的作用,于是我就去找了度娘。终于,我对int函数有了更深点的理解,int型函数是向下取整函数,例如:int(3.8)=3,int(-3,8)=-4,主体强调的是向下取整。范围[-2^31 , 2^31 - 1] 即 [-2147483648,2147483647]。然而这道题涉及到了小数,所以我就顺势将两个都定义成了float型,但是依然无法顺利编译,细细品来(在我看来,代码是艺术,需要品味),发现我犯了一个不经常涉及的小问题,那就是在“1.0/i”我写成了“1/i”,于是知错就改的我及时更正了问题,这是问题又出现了,我再次编译,答案是4(因为我一开始为了检验代码,所以条件语句中条件写的是“sum>=2.0”),正确,于是我就又改成了“sum>=15.0”,奇迹出现了,答案竟然是一串不明字符“1.67386e+06”,顿时我蒙圈了,心里想着怎么破怎么破,此时,我开始理思路,前面的几个都成功,为什么到这里却不行了呢?会不会是答案范围的问题?答案是i,那么会不会是i的范围的问题?i是浮点型……什么,等等,我为啥要用浮点型呢?明明输出的答案都是整形,我却用的浮点型,先不管其是否可以编译成功,就这个情况定义这个类型也不符合代码的规范性啊!于是抱着试一试的心态的我将i改为了int型,奇迹再次出现,编译成功了,结果是“1673859”,正确。虽说这个程序成功了,但是我依然心存疑虑为什么数据一大,用float型就失败了呢?是不是也是数据范围的问题?于是,度娘,我又来了……

问过度娘后,我总算是明白了,浮点数使用 IEEE(电气和电子工程师协会)格式。浮点类型的单精度值具有 4 个字节,包括一个符号位、一个 8 位 excess-127 二进制指数和一个 23 位尾数。尾数表示一个介于 1.0 和 2.0 之间的数。由于尾数的高顺序位始终为 1,因此它不是以数字形式存储的。此表示形式为 float 类型提供了一个大约在 3.4E–38 和 3.4E+38 之间的范围。

范围的问题导致了输出结果的紊乱,看来这道题的含金量也是有的,但主要不是考察我们的算法,而是考察我们的细心程度与对整型和浮点型的认知深度。

真是辛苦出题人了,向出题人致敬!


2017.3.26修正

这是我初学编程时的一些小插曲,现在看看那时写的博客,感觉自己十分幼稚,但是这大概是初学者毕竟的一个过程吧~~~评论区有朋友说我的代码错了,当时我也只是写出来这个代码并没有提交,那时候对 OJ 还没有什么概念,以为自己出结果就是正确的,现在看来真是滑稽的狠啊,这里的确不能用 float,数据小的时候还没问题,但是当数据大的时候,导致小数特别的小,已经无法用 float 完美的解决它了,只能动用更高精度的双精度,毕竟 float 只能表示六位左右有效数字,而 double 则能表示15位左右,如果没有记错的话,感谢评论区的小伙伴给我指出我曾经的问题,以前的博文我也不想做什么修改,毕竟这是我曾经的故事啊,所以只修改了文中代码,为了防止有的朋友只看看代码就走了,这样我就可能提供了一个误人子弟的代码啦,我担不起责任,所以只修正一下代码,如果愿意看我白话的人的话,那么他一定会看到我这里的重新解释的。