关于作业批改的Review Comment

在comment中,只会指出如下的问题:

  1. 显然的错误
  2. 不常见的错误
  3. 一些代码风格问题
  4. 与作业要求的不符的问题

共性的错误,请大家参考下方的代码示例自行修正。

作业修正后不需要再提交,作业没有评分,只要你的作业态度端正且能按时提交即可。

如果你这段时间特别忙,可以联系老师/助教,或直接在作业提交的地方进行特殊说明。

代码示例

下载链接

task1

if

常见问题:

  1. 逻辑语句错误:如 a < b < cif (a = 1)

⚠️ in * r / 100 or in * r / 100.0

如果设定了:

int in, r;

那么这句话会导致一些舍入的误差。

switch

常见问题:

  1. 缺少break语句

task2

常见问题:

  1. 没有判断二次方程、没有解复根
  2. 没有用fabs,见faq

task3

常见问题:

  1. 缺少对0的处理
  2. 如果while条件是n > 0,那还需要有n输入为正数的条件

task4

常见问题:

  1. 缺少对0的处理
  2. 很多同学直接用了欧几里德法

和欧几里德算法相关的理论可以参考:最大公约数

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;
}

输入:

  1. a = 13, b = 7
  2. a = 1300, b = 0.07
  3. a = 130000 , b = 0.0007

大家可以看到,第三种情况下,a+ba-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

变量命名

在任何编程中,常用类似于数学中的变量命名方法:

  1. 整型变量:n, m, k
  2. 下标变量:i, j, k
  3. 浮点变量:x, y, z、拉丁字符等

a, b, c等变量的类型通常视算法而定。

关于if、for等后接 {}包围的代码块的语句

⚠️ 始终在所有 ifforwhile 等语句中使用{ }来包裹你的代码块!

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