数值分析作业 - 浮点运算和误差
数值分析Problem 1
说明
分析单精度计算 fl(9.4)−fl(9)−fl(0.4)
的结果,并进行计算机实践。
Symbol | Exponent | mantissa | |
---|---|---|---|
9.4f |
+ 0 |
3 + 127 = 10000010 |
100101100110011001100110 |
9.0f |
+ 0 |
3 + 127 = 10000010 |
100100000000000000000000 |
9.4f-9.0f |
+ 0 |
3 + 127 = 10000010 |
000001100110011001100110 |
Lsh 5 | + 0 |
-2 + 127 = 01111101 |
110011001100110011000000 |
0.4f |
+ 0 |
-2 + 127 = 01111101 |
110011001100110011001101 |
9.4f-9.0f-0.4f |
- 1 |
-2 + 127 = 01111101 |
000000000000000000001101 |
Lsh 20 | - 1 |
-22 + 127 = 01101001 |
110100000000000000000000 |
得到计算结果为
编写辅助函数用于输出浮点数的比特
1 |
|
验证计算过程
1 | print_fpbits(9.4f); |
得到输出
1 | 9.4f: |
Problem 2
说明
设计高效的多项式算法
容易写出直接的
1 | const double coef[] = {1, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5}; |
和简单的
1 | double f_fast(double x) { |
需要注意的是, 针对 C++ 一类编译器具有强大静态优化能力的编程语言, 将简单的算法在同样的输入下单纯地重复运行并不是合理的性能测量方式, 因为编译器很容易将整个循环删除掉. 简单起见, 可以在循环中要求从 std::vector
中读取并存储数据, 以防止无用的循环被删除. 下面报告程序在 MSVC 19.33.31629.0
下, 简单循环
1 | x = 2, naive: 2272.14ms |
启用 /O2
优化后, 性能有显著的提升
1 | x = 2, naive: 686.996ms |
Problem 3
说明
推导计算
容易推导公式
初始值可以取
这个正向递推公式每次递推将误差放大五倍
1 | forward = (NestList[{E - #[[2]] #[[1]], #[[2]] + 1} &, {1., 2}, 20] // Transpose)[[1]]; |
1 | {1., 0.7182818284590451, 0.5634363430819098, |
注意到在 16 项处由于误差的累积导致积分有界性已被破坏, 之后的迭代过程明显不收敛.
考虑反向迭代公式
取
1 | backward = (NestList[{1/#[[2]] (E - #[[1]]), #[[2]] - 1} &, {1., 30}, |
1 | {1., 0.7182818284590452, 0.5634363430819095, |
与 NIntegrate
直接积分 (保证 20 位有效数字) 的结果比较并绘制图像
1 | direct = |
![](https://cdn.duanyll.com/img/20230312161016.png)
绘制两种算法和直接数值积分法的残差
1 | ListLinePlot[ |
![](https://cdn.duanyll.com/img/20230312161441.png)
由于反向迭代逐渐减小误差的特性, 表现远好于正向迭代