关于浮点数
请先阅读下面的程序,并猜测输出:
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include <iostream> #include <cmath> const double eps = 1e-6; double x = 5.2, y = 5.1 + 0.1; int main() { printf("%d\n", x == y); printf("%d\n", x < y); printf("%d\n", x > y); printf("%e\n", x - y); printf("%d\n", fabs(x-y) < eps); printf("%d\n", x > y + eps); return 0; }
|
大多数同学预测的输出应该是:
然而,正确的输出应该是:
是不是令你大吃一惊呢?让我来解释一下这个现象的原因。说到底,就是一个精度问题。
系统中的数据都是以二进制形式存储的。
例如:\(114514 =
2^{1}+2^{4}+2^{6}+2^{8}+2^{9}+2^{10}+2^{11}+2^{12}+2^{13}+2^{15}+2^{16}\)
在存储小数的时候,可能会遇到精度不够的问题。这就导致了在存储时与原数会有一点点的差距。差距一般在
\(0.000001\)
左右。所以,当我们想要判断两个浮点数是否相等时,只需判断两数之差是否小于
\(0.000001\)
即可。如果小于,则判断两个浮点数相等(如前面的程序的第 \(10\) 行所示)。
同理,我们可以推出 \(>\ <\ \leq\
\geq\) 的判断式(\(eps=10^{-6}\)):
判断浮点数 == |
fabs(x-y) < eps |
判断浮点数 > |
x > y + eps |
判断浮点数 < |
x < y - eps |
判断浮点数 >= |
x > y - eps |
判断浮点数 <= |
x < y + eps |
关于输出
输出,那各位可再熟悉不过了。可别小看了输出,其中依然有坑。
常见的输出函数有四种:cout
、printf
、puts
、putchar
。关于它们的用法,请出门左转到
CSDN。
我们还是先来看一段程序。这段程序从是我的 这篇博客
中摘录的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| #include <iostream> using namespace std; const int N = 32; int n, a[N]; bool isFirst = true; string s[N] = {"", "11/(45-1)*4", "-11+4-5+14", "-11-4+5+14", "11-4+5/1-4", "11-4-5+14", "1*1+45-14", "11*4+5*1*4", "1+1+(4+5)*14", "(114-51)*4+(-11-4+5+14)", "1+1-4+514", "1*(1+4)*51*4+(-11-4+5+14)", "-11+4*514+(11*-4+51-4)", "(1+1)*451*4+(11*(45-1)+4)", "114*5*14+((1+1)*4+51*4)", "1145*14+((11-4)*51-4+(11/(45-1)*4))", "114*51*4+((1+1)*4514+(11*45-14+(11*-4+51-4)))", "114*514+(11*45*14+(-11/4+51/4))", "114514+(1145*14+(1*14+514))", "114514*(-11+4-5+14)+(114*51*4+((1+1)*4514+(11+4*51*4+(11-4*5+14))))", "114514*(-11-4+5+14)+(114*514+(1+14*514+((1+145)*-(1-4)+(11/(45-1)*4))))", "114514*(11-4+5+1-4)+(1145*14+((11+451)*4+(-1-1+4+5*14)))", "114514*(1+1+4*5*1-4)+(114*51*4+(11451+4+(1145+14+(1*-1+45-14))))", "114514*(11+4*5+1+4)+(114*514+(11451+4+(114*-5*(1-4)+(-11+45+1+4))))", "114514*(1*14*5-1+4)+(114*51*4+(114*51+4+(-11+4+5+14)))", "114514*(11+45*-(1-4))+(11*4514+(114*5*14+(1+14+514+(11-4+5+1-4))))", "114514*(11+4*5*14+(-11+4-5+14))+(114*(5-1)*4+(1-14+5+14))", "114514*(114*5+14+(-11+4-5+14))+((1+1)*451*4+(11+45/1-4))", "114514*(1145+14+(1*14-5/1+4))+(1+14*514+(114-5+14))", "114514*(114*5*1*4+(11*4+5*1*4))+(1+14514+(-1-14*(5-14)))", "114514*((1145+1)*4+(114-5-1-4))+(114*51*4+(114*51+4+(11*4*5-14)))", "114514*((1+1)*4514+(11*(45-14)+(11-4+5-1-4)))+(11*4514+(114*5*14+(-(1-14)*5*14+(11-4-5+14))))"}; void print(int x) { if (isFirst) { isFirst = false; } else { if (s[x][0] != '-') cout << '+'; } cout << s[x]; } void BinarySplit(int x) { for (int i = 30; i >= 0; i--) if ((1<<i) & x) print(i+1); } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin >> n; BinarySplit(n); return 0; }
|
这是正解程序。但是,一开始,我的 print
函数是这样写的:
1 2 3 4 5 6 7 8
| void print(int x) { if (isFirst) { isFirst = false; } else { if (s[x][0] != '-') putchar('+'); } cout << s[x]; }
|
乍一看,似乎没有什么区别。但当我随便输入一个数(例如 \(114514\))时,输出是这样的:
1
| ++++++++114*514+(11*45*14+(-11/4+51/4))114*51*4+((1+1)*4514+(11*45-14+(11*-4+51-4)))114*5*14+((1+1)*4+51*4)(1+1)*451*4+(11*(45-1)+4)-11+4*514+(11*-4+51-4)1*(1+4)*51*4+(-11-4+5+14)1+1-4+514(114-51)*4+(-11-4+5+14)11*4+5*1*411-4-5+14-11+4-5+14
|
我调了半天,也不知道哪里出了问题,索性把 putchar
改成了
cout
。结果发现,问题竟然解决了!
1
| 114*514+(11*45*14+(-11/4+51/4))+114*51*4+((1+1)*4514+(11*45-14+(11*-4+51-4)))+114*5*14+((1+1)*4+51*4)+(1+1)*451*4+(11*(45-1)+4)-11+4*514+(11*-4+51-4)+1*(1+4)*51*4+(-11-4+5+14)+1+1-4+514+(114-51)*4+(-11-4+5+14)+11*4+5*1*4+11-4-5+14-11+4-5+14
|
当时我就懵了。怎么会这样呢?这个问题我到现在也没搞懂。如果有大神知道原因,请不吝指教,谢谢!
总之,这个例子说明了:千万不要把各种输出混用。C
风格就全程 C
风格,C++
就全程
C++
。