Week 4 - Assignment Solution
关于作业批改的Review Comment
在comment中,只会指出如下的问题:
- 显然的错误
- 不常见的错误
- 一些代码风格问题
- 与作业要求的不符的问题
共性的错误,请大家参考下方的代码示例自行修正。
作业修正后不需要再提交,作业没有评分,只要你的作业态度端正且能按时提交即可。
如果你这段时间特别忙,可以联系老师/助教,或直接在作业提交的地方进行特殊说明。
代码示例
task1
if
常见问题:
- 逻辑语句错误:如
a < b < c
、if (a = 1)
⚠️ in * r / 100
or in * r / 100.0
如果设定了:
int in, r;
那么这句话会导致一些舍入的误差。
switch
常见问题:
- 缺少
break
语句
task2
常见问题:
- 没有判断二次方程、没有解复根
- 没有用
fabs
,见faq
task3
常见问题:
- 缺少对0的处理
- 如果
while
条件是n > 0
,那还需要有n
输入为正数的条件
task4
常见问题:
- 缺少对
0
的处理 - 很多同学直接用了欧几里德法
和欧几里德算法相关的理论可以参考:最大公约数
FAQ
fabs相关
机器浮点误差的来源:所有的浮点数都具有有限精度,通常浮点数的误差用相对误差来描述,假设真值为 \(x^*\),机器值为\(x\),那么相对误差定义为\(e_r = (x ^ * - x) / x^*\)。
float
类型通常\(e_r \sim 1e -4\)double
类型通常\(e_r \sim 1e-7\)
我们用简单的程序实验该误差:
#include <iostream>
int main () {
float a, b;
std::cin >> a >> b;
std ::cout << "a + b = "<< (a + b) << std ::endl;
std ::cout << "a - b = "<< (a - b) << std ::endl;
std ::cout << "a * b = "<< (a * b) << std ::endl;
std ::cout << "a / b = "<< (a / b) << std ::endl;
return 0;
}
输入:
a = 13, b = 7
a = 1300, b = 0.07
a = 130000 , b = 0.0007
大家可以看到,第三种情况下,a+b
,a-b
都出现了误差,出现了大数吃小数的问题。
实际上除法也会出现较大的误差。
但需要使用
printf
函数来输出浮点数的值,而非使用cout
,因为cout
执行了一些输出优化。
实际计算中,还有很多种可能产生浮点误差的情况。
所以什么样的两个数是相等的?考虑一个绝对误差限\(\varepsilon\),相等定义为: \(|x - y| < \varepsilon\) 即要求使用的
if (fabs(x - y) < 1e-7) {
...
}
但利用了该定义后,小于、大于等比较运算为了不与相等出现语义冲突:
\[less~than \iff x < y - \varepsilon\\ greater~than \iff x > y + \varepsilon\]对于机器中的两个浮点数,除非直接赋值,否则几乎不可能相等。
== 和 =
老生常谈的问题:
x = 1
也是一个表达式,值为1
因此
if (x = 1) {
always_do();
} else {
always_wont_do();
}
a < b < c
不支持这个类python的比较方法。
使用了未初始化的局部变量
声明变量:
int x;
后,其值是不确定的。直接使用该值会导致一些不可控的问题。
因此:
int x, y;
cin >> x; // no error.
x = x + y; // y is not initialized, use the value of y is illegal
变量命名
在任何编程中,常用类似于数学中的变量命名方法:
- 整型变量:
n, m, k
等 - 下标变量:
i, j, k
等 - 浮点变量:
x, y, z
、拉丁字符等
a, b, c
等变量的类型通常视算法而定。
关于if、for等后接 {}包围的代码块的语句
⚠️ 始终在所有 if
、for
、while
等语句中使用{ }
来包裹你的代码块!
Apple Goto Fail
:当年apple写了一段这样的代码……
void func() {
bool fail;
if (cond1)
fail = true;
if (cond2)
fail = true;
fail = true; // Here is the bug
if (! fail) {
do_something(); // Always wont do.
}
return;
}
为什么我缩进了但是循环中不执行这条语句?
同上。c++使用{}
来表示作用域
尽量不使用全局变量
为什么整数次方$x^n$不用 pow(x, n)
pow
函数内部调用的是Taylor 展开来求解方幂,整数次方幂不需要使用这个较为低效的算法。
到底应该用<cmath>
还是<math.h>
对于 C++ 标准库,始终不使用类似于xxx.h
的头文件:
xxx.h
的头文件是给C语言的代码使用的cxxx
的头文件具有与xxx.h
相同的功能,但是是给C++代码使用的,具有一些更新的特性。
为什么代码缩进很重要
See Apple Goto Fail