作为我的一门类(class)的一部分,我应该通过打印 1./3.
的结果来证明使用 C++ 进行双除法可能会导致精度损失。保留20位小数,并标出非3的位置。
但是,我的算法(我在下面列出)似乎一直遇到相同的运行时错误。
我在下面留下了代码以及下面的预期和实际输出。有没有解决的办法?我尝试了其他方法(包括 ostringstream
中的 <sstream>
),但这是迄今为止我能想到的最准确的方法。
算法
- 存储
1./3.
的结果到一个变量。 - 打印出
1./3.
保留到 20 位小数并添加适当的间隔符。 - 将第 1 步中的变量乘以 10(即将所有数字左移一位)。
- 检查个位数是否为 3。如果是,打印
;否则,打印
^
. - 递减个位直到个位为0。
- 再重复步骤 3-6 19 次。
预期输出
1./3. Precision test
1./3.: 0.33333333333333331483
^^^
实际输出(带调试输出)
1./3. Precision test
1./3.: 0.33333333333333331483
^ ^^
33333333333333330372
#include <iostream>
#include <math.h>
#include <cmath>
#include <iomanip>
using namespace std;
int main()
{
double third = 1./3.;
cout << "1./3. Precision test" << endl;
cout << setw(7) << "1./3.: " << setprecision(20) << third << endl;
cout << " ";
for (int i = 1; i <= 20; i++) {
third *= 10;
if (int(third) == 3)
cout << " ";
else
cout << "^";
while (third >= 1) third -= 1;
}
cout << endl;
// For debugging purposes
third = 1./3.;
cout << " ";
for (int i = 1; i <= 20; i++) {
third *= 10;
cout << int(third);
while (third > 1) third -= 1;
}
cout << endl;
}
最佳答案
您陷入了精度问题的陷阱。如果您重复将 1/3 乘以 10,将得到以下结果:
0.33333333333333331483
3.3333333333333330373
33.333333333333328596
333.33333333333325754
3333.3333333333325754
33333.333333333328483
333333.33333333325572
3333333.3333333325572
33333333.333333324641
333333333.33333325386
3333333333.3333325386
33333333333.333324432
333333333333.33325195
3333333333333.3325195
33333333333333.324219
333333333333333.25
3333333333333332.5
33333333333333324
333333333333333248
3333333333333332480
33333333333333323776
请注意,最后一位数字正在发生变化。您需要先转换为字符串,如下所示:
double third = 1. / 3.;
cout << "1./3. Precision test" << endl;
ostringstream s;
cout << setw(7) << "1./3.: ";
s << setprecision(20) << third << endl;
string str = s.str();
cout << str;
cout << " ";
for (int i = 2; i <= 21; i++) {
if (str[i] == '3')
cout << " ";
else
cout << "^";
}
cout << endl;
关于c++ - 在 C++ 中标记 double 损失的算法不会标记正确的位置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35545951/