设置精度工作不正常

set precision isnt working properly

提问人:Anmol Sethi 提问时间:1/12/2013 最后编辑:Jason CAnmol Sethi 更新时间:8/11/2014 访问量:6245

问:

#include <iostream>
using namespace std;

int main()
{
    cout.precision(50);
    cout<<"Anmol's Square Root Calculator!"<<endl;
    cout<<""<<endl;
    cout << "This program will compute the square root\n";
    cout << "of a number using the Babylonian algorithm!\n";
    cout<<""<<endl;
    cout<<"Only positive numbers work with this algorithm!"<<endl;
    cout<<""<<endl;
    cout<<"All cycle #1 approximate square roots are guessed\n"<<"using 1 as the first        approximate."<<endl;
    cout<<""<<endl;
    cout<<""<<endl;
    cout<<""<<endl;



    char playAgain='y';
    while (playAgain !='n') 
    {
        int count(25), cycle(1);
        double guess, sqrt, num; 
        cout << "Please enter the number you would like to compute the square root of: ";
        cin >> num;
        cout<<""<<endl;

        do {
            if (num <= 0)
            {
                cout<<""<<endl;
                cout<<"Invalid input. Please re-enter a valid number: ";
                cin >> num;
                cout<<""<<endl;
            }
        } while (num <= 0);

        cout<<""<<endl;
        cout<<""<<endl;
        cout<<""<<endl;

        for (guess=1; count!=0; count--)
        { 
            guess =(guess + (num/guess))/2;
            cout<<""<<endl;
            cout<<"Cycle "<<cycle<<" Aproximate: "<<guess<<endl;
            sqrt = guess;

            cycle++;
        }

        cout<<""<<endl;

        cout << "The square root of "<< num << " is " << sqrt<<"!"<<endl;;
        cout <<""<< endl;

        do { 
            cout <<""<<endl;
            cout << "Would you like to calculate again? (y/n): ";
            cin >> playAgain;
            cout <<" "<<endl;
            do { 
                cout <<""<<endl;
                if ((playAgain !='y' && playAgain !='n'))
                {
                    cout << "Invalid input. Only y/n: ";
                    cin >> playAgain;
                    cout <<" "<<endl;
                } 
            } while (playAgain !='y' && playAgain !='n');

        } while (playAgain !='y' && playAgain !='n');

    }

    cout<<"Thank you for using a program made by Anmol Sethi!"<<endl;
    cout<<""<<endl;
    cout<<""<<endl;
    cout<<""<<endl;
    cin.get();
    return 0;
}

在我的数学课上,我们正在学习平方根。根据我的老师的说法,只能使用计算器或表格找到平方根,这显然是错误的,因为计算器需要一种方法来计算平方根。经过广泛的研究,我使用巴比伦算法制作了这段代码。但是我面临一个问题 在此代码中,我专门将小数点的精度设置为 15。但是,它以 12 位小数输出数字。但在此代码中,它以 15 位小数输出。

#include <iostream>
using namespace std;

int main()
{
    cout.precision(18);
    cout<<"Anmol's Square Root Calculator!"<<endl;
    cout<<""<<endl;
    cout << "This program will compute the square root\n";
    cout << "of a number using the Babylonian algorithm!\n";
    cout<<""<<endl;
    cout<<"Only positive numbers work with this algorithm!"<<endl;
    cout<<""<<endl;
    cout<<"All cycle #1 approximate square are guessed\n"<<"using 1 as the approximate."<<endl;
    cout<<""<<endl;
    cout<<""<<endl;
    cout<<""<<endl;
    char playAgain='y';
    while (playAgain !='n') 
    {
        int count(25), cycle(1);
        double guess(1), sqrt, x, num; 
        cout << "Please enter the number you would like to know the square root of: ";
        cin >> num;
        cout<<""<<endl;

        do {
            if (num <= 0)
            {
                cout<<""<<endl;
                cout<<"Invalid input. Please re-enter a valid number: ";
                cin >> num;
                cout<<""<<endl;
            }
        } while (num <= 0);

        cout<<""<<endl;
        cout<<""<<endl;
        cout<<""<<endl;

        while (count > 0)
        {
            x = guess =(guess + (num/guess))/2;
            cout<<""<<endl;
            cout<<"Cycle "<<cycle<<" Aproximate: "<<guess<<endl;
            sqrt = guess;

            count-=1;
            cycle+=1;
        }

        cout<<""<<endl;
        cout<<""<<endl;
        cout<<""<<endl;
        cout << "The sqaure root of "<< num << " is " << sqrt<<"!"<<endl;;
        cout <<""<< endl;

        do { 
            cout <<""<<endl;
            cout << "Would you like to calculate again? (y/n): ";
            cin >> playAgain;
            cout <<" "<<endl;

            do { 
                cout <<""<<endl;
                if ((playAgain !='y' && playAgain !='n'))
                {
                    cout << "Invalid input. Only y/n: ";
                    cin >> playAgain;
                    cout <<" "<<endl;
                } 
            } while (playAgain !='y' && playAgain !='n');

        } while (playAgain !='y' && playAgain !='n');

    }

    cout<<"Thank you for using a program made by Anmol Sethi!"<<endl;
    cout<<""<<endl;
    cout<<""<<endl;
    cout<<""<<endl;
    cout<<""<<endl;
    cin.get();
    return 0;

}

我对为什么会发生这种情况感到非常困惑。我尝试了 cout << 固定<<展示点;但是当 NUM 无缘无故输出 15 个零时,它看起来很不愉快。谁能告诉我为什么会发生这种情况以及如何解决它?在我读到的一本书中,对算法使用 for 循环更有效,所以我想使用 for 而不是 while 循环。我还有第二个问题。为什么我不能超过小数点后 15 位?如果我将精度设置为 30/40,即使在我的计算机计算器中它变为 30/40,它也不会对我的输出产生任何影响。我应该使用另一种整数类型来代替双精度类型吗?

对不起,如果它看起来像一堵大墙的文字 xD。

我急于输入这个,因为它是我的 12:05am atm。

提前致谢:D

p.s 我现在将在这个论坛上发布很多内容。大约一年前,我曾经经常使用它,但我退出了一段时间的 c++ 编程,因为当我还是较小的 xD 时,我无法进一步理解它。所以我现在正在读一本书,我希望能一直通过控制台应用程序进入 Windows。如果重要的话,我正在使用 Visual Studio Ultimate 2012。xD

C++ 十进制 精度

评论

0赞 daniel gratzer 1/12/2013
请正确缩进,阅读起来要容易得多。 在Linux上很好fmt
0赞 sehe 1/14/2013
或者 astyle、cident、bcpp 等等。只需使用 vim、emacs、sublime-text、任何 IDE(eclipse、code::blocks、MSVS Express 等),它都是内置的,而且是免费的。

答:

7赞 Nate Chandler 1/13/2013 #1

首先,欢迎来到 StackOverflow!我相信,当你学习编程时,你会发现它是一种宝贵的资源。也就是说,要充分利用该网站,遵守该网站的指南非常重要。一般来说,请记住,SO不是一个论坛,而是一个问答网站,你会做得很好。


一般问题

您发布的代码存在一些问题:

A. 空白

使用空格对于使您的代码对其他程序员和您自己都清晰易懂非常有帮助。Tab 键指示范围(写入

bool condition = ...
if (condition) {
    action();
}

而不是

bool condition = ...
if (condition) {
action()
}

这并不表示操作范围)会显著提高可读性,尤其是当您发布的代码中有多个嵌套范围时。间距也很重要。

B. 使用cout

1.与其写,不如简单地写。cout << "" << endlcout << endl

2.endl“\n” 之间存在差异,从您的代码中可以看出您可能知道它们。虽然使用两者的性能特征并不完全相同,但考虑到现阶段的优化肯定为时过早。对于这种日志记录,坚持使用更具可读性。而不是endl

cout << "All cycle #1 approximate square are guessed\n" << "using 1 as the approximate." << endl;

最好写

cout << "All cycle #1 approximate square are guessed" << endl << "using 1 as the approximate." << endl;

在这种情况下。

3.如果你在一行代码中插入 int 绰绰有余(此代码,cout

cout << "This line of code is longer than feels comfortable." << endl << "It would be much more pleasant to break this into several lines so that I don't need to scroll to the right to view the whole thing." << endl;

例如,对于一行来说太长了),而不是像在代码中那样,

cout << "Anmol's Square Root Calculator!" << endl;
cout << endl;
cout << "This program will compute the square root\n";
cout << "of a number using the Babylonian algorithm!\n";
cout << endl;
cout << "Only positive numbers work with this algorithm!" << endl;
cout << endl;
cout << "All cycle #1 approximate square are guessed\n" << "using 1 as the approximate." << endl;
cout << endl;
cout << endl;
cout << endl;

多次使用 insertion 运算符 on,只需将cout

cout << "Anmol's Square Root Calculator!" << endl
     << endl
     << "This program will compute the square root" << endl
     << "of a number using the Babylonian algorithm!" << endl
     << endl
     << "Only positive numbers work with this algorithm!" << endl
     << endl
     << "All cycle #1 approximate square are guessed"  << endl
     << "using 1 as the approximate." << endl
     << endl
     << endl
     << endl;

C. 模块化

您的所有代码都在 .即使使用如此短的程序,这仍然会妨碍可读性。更重要的是,这意味着您不能在其他地方重用在此程序中创建的功能。main()

一个好的方法是将程序分解为单独的组件。在此程序中,您已获得以下设置:

show a greeting
do {
    get number from user
    compute the squareroot of that number while logging
    show the user the squareroot
    ask the user if they want to do another computation
} while the user wants the program to keep running
show a farewell

此时,您可以为每个步骤编写函数。我在这里“编写”的函数是从您的代码中摘录的,并进行了非常轻微的修改。

打印到屏幕

向用户表示问候和告别的功能是最简单的,因为它们(几乎)只需要使用:cout

void showGreeting()
{
    cout << "Anmol's Square Root Calculator!" << endl
         << endl
         << "This program will compute the square root" << endl
         << "of a number using the Babylonian algorithm!" << endl
         << endl
         << "Only positive numbers work with this algorithm!" << endl
         << endl
         << "All cycle #1 approximate square are guessed"  << endl
         << "using 1 as the approximate." << endl
         << endl
         << endl
         << endl;
}

void showFarewell()
{
    cout << "Thank you for using a program made by Anmol Sethi!" << endl
         << endl
         << endl
         << endl;
    cin.get();
}

获取用户输入

接下来,我们需要一个函数来从用户那里获取一个数字

double getInputNumber()
{
    double num = 0.0f;

    cout << "Please enter the number you would like to compute the square root of: ";
    cin >> num;
    cout << endl;

    do {
        if (num <= 0)
        {
            cout << endl
                 << "Invalid input. Please re-enter a valid number: ";
            cin >> num;
            cout << endl;
        }
    } while (num <= 0);

    cout << endl
         << endl
         << endl;

    return num;
}

此外,我们需要确定用户是否希望程序再次运行。从程序的角度来看(从),这在很大程度上是一个布尔问题:我们应该调用某个函数,它应该返回 or。但是,在函数中,我们正在与用户交互,因此使用 and 是理想的(至少对于说英语的用户而言);maintruefalse'y''n'

bool getRunAgain()
{
    bool choice = false;

    char playAgain = 'n';
    do { 
        cout << endl
             << "Would you like to calculate again? (y/n): ";
        cin >> playAgain;
        cout << endl;
        do { 
            cout << endl;
            if ((playAgain !='y' && playAgain !='n'))
            {
                cout << "Invalid input. Only y/n: ";
                cin >> playAgain;
                cout << endl;
            } 
        } while (playAgain !='y' && playAgain !='n');
    } while (playAgain !='y' && playAgain !='n');

    if (playAgain == 'y') {
        choice = true;
    } else /*if (playAgain == 'n')*/ {//At this, playAgain is either 'y' or 'n'.  So if it's not 'y', it's 'n'.
        choice = false;
    }
    return choice;
}

由于将此代码重写为函数,因此更容易看出这里所做的工作比必要的工作要多:考虑外部循环。它重复的条件是 .乍一看,这似乎是有道理的:我们需要确保用户输入了“是”或“否”。但请注意,有一个内部循环具有完全相同的条件。这意味着内部循环不会退出,除非等于 或 。因此,当我们离开内部循环时,我们可以确定是 或 。所以我们不需要再次检查它。这允许我们像这样重写函数:do...whileplayAgain !='y' && playAgain !='n'do...whileplayAgain'y''n'playAgain'y''n'

bool getRunAgain()
{
    bool choice = false;

    char playAgain = 'n';
    cout << endl
         << "Would you like to calculate again? (y/n): ";
    cin >> playAgain;
    cout << endl;
    do { 
        cout << endl;
        if ((playAgain !='y' && playAgain !='n'))
        {
            cout << "Invalid input. Only y/n: ";
            cin >> playAgain;
            cout << endl;
        } 
    } while (playAgain !='y' && playAgain !='n');

    if (playAgain == 'y') {
        choice = true;
    } else /*if (playAgain == 'n')*/ {
        choice = false;
    }
    return choice;
}

但是在剩下的循环中,我们也有类似的情况:

do { 
    cout << endl;
    if ((playAgain !='y' && playAgain !='n'))
    {
        cout << "Invalid input. Only y/n: ";
        cin >> playAgain;
        cout << endl;
    } 
} while (playAgain !='y' && playAgain !='n');

除了 这 不是 太 重要 之外,看起来我们根本不想进入循环,除非 .这需要一个循环而不是循环:cout << endl(playAgain !='y' && playAgain !='n')whiledo...while

while (playAgain !='y' && playAgain !='n')
{
    cout << "Invalid input. Only y/n: ";
    cin >> playAgain;
    cout << endl;
}

把它放到我们的函数中,我们现在有了

bool getRunAgain()
{
    bool choice = false;

    char playAgain = 'n';
    cout << endl
         << "Would you like to calculate again? (y/n): ";
    cin >> playAgain;
    cout << endl;

    while (playAgain !='y' && playAgain !='n')
    {
        cout << "Invalid input. Only y/n: ";
        cin >> playAgain;
        cout << endl;
    }

    if (playAgain == 'y') {
        choice = true;
    } else /*if (playAgain == 'n')*/ {
        choice = false;
    }
    return choice;
}

计算平方根

最后,我们需要一个函数来使用巴比伦算法计算平方根。我们只需要获取您的代码

int count(25), cycle(1);
double guess, sqrt, num; 
cout << "Please enter the number you would like to compute the square root of: ";
cin >> num;
cout << endl;

for (guess=1; count!=0; count--)
{ 
    guess =(guess + (num/guess))/2;
    cout<<""<<endl;
    cout<<"Cycle "<<cycle<<" Aproximate: "<<guess<<endl;
    sqrt = guess;

    cycle++;
}

并将其放入一个函数中,该函数取 double 并返回 1:

double computeSquareRootBabylonian(double num)
{
    int count(25), cycle(1);
    double guess, sqrt; 

    for (guess = 1; count != 0; count--)
    { 
        guess = (guess + (num/guess))/2;
        cout << endl
             << "Cycle " << cycle << " Aproximate: " << guess << endl;
        sqrt = guess;

        cycle++;
    }
    return sqrt;
}

我们可以到此为止,但有一些更改可以改进功能。

1.初始近似值(您正在使用的初始猜测)并不是此函数的真正组成部分。调用此函数的代码应指定该起点:1

double computeSquareRootBabylonian
(double num, 
 double initialApproximation)
{
    double approximation, sqrt;
    unsigned int count(25), cycle(1);

    for (approximation = initialApproximation; count != 0; count--)
    {
        approximation =(approximation + (num/approximation))/2;
        cout << endl
             << "Cycle " << cycle << " Aproximate: " << approximation << endl;
        sqrt = approximation;

        cycle++;
    }
    return sqrt;
}

2.迭代次数(或循环,正如你所说的那样)也不是函数的真正组成部分。调用此函数的代码应指定该数字:

double computeSquareRootBabylonian
(double num, 
 double initialApproximation, 
 unsigned int iterations)
{
    double approximation, sqrt;
    unsigned int iterationsRemaining = iterations;
    unsigned int iteration = 1;

    for (approximation = initialApproximation; iterationsRemaining != 0; iterationsRemaining--)
    {
        approximation =(approximation + (num/approximation))/2;
        cout << endl
             << "Cycle " << iteration << " Aproximate: " << approximation << endl;
        sqrt = approximation;

        iteration++;
    }
    return sqrt;
}

3.这个循环很奇怪。这很奇怪,因为你在初始化中初始化了变量(我已经重命名了),尽管它不是“循环变量”。在第二个代码块中,你使用了forguessapproximation

while (count > 0)
{
    guess =(guess + (num/guess))/2;
    cout << endl;
    cout << "Cycle " << cycle << " Aproximate: " << guess << endl;
    sqrt = guess;

    count-=1;
    cycle+=1;
}

相反。这段代码的意图要清晰得多,但使用标准方式的循环(在初始化中初始化循环条件所依赖的相同变量,并在每个循环结束时更改:)。这里迭代的变量是(我称之为 ),只要它大于 ,它就会从用户指定的迭代次数(参数)中减少。这种情况需要以下 for 循环:forfor (int i = 0; i < 10; i++)countiterationsRemainingiterations10

for (unsigned int iterationsRemaining = iterations; 
     iterationsRemaining > 0; 
     iterationsRemaining--) 
{
    //...
}

当将其代入我们的函数时,我们需要确保仍然初始化为:approximationinitialApproximation

double computeSquareRootBabylonian
(double num, 
 double initialApproximation,
 unsigned int iterations)
{
    double approximation = initialApproximation;
    unsigned int iteration = 1;
    double sqrt;

    for (unsigned int iterationsRemaining = iterations; 
         iterationsRemaining > 0; 
         iterationsRemaining--) 
    {
        approximation =(approximation + (num/approximation))/2;
        cout << endl
             << "Cycle " << iteration << " Aproximate: " << approximation << endl;
        sqrt = approximation;

        iteration++;
    }
    return sqrt;
}

题外话:更过早的优化

你写了

在我读到的一本书中,对算法使用 for 循环更有效,所以我想使用 for 而不是 while 循环。

这不是使用循环的好理由。你不需要(在合理范围内)对你的代码进行更改以使其更快,除非你确定它运行得太慢。编写在阅读时有意义的代码更为重要。for

4.变量有什么作用?它重复具有赋值 of 并从函数返回,但返回时的值与 的值相同。由于这个原因而删除它相当于过早的优化。但是,调用该变量表明它是真正的平方根,而不是近似值。出于这个原因,它应该被删除,给我们留下sqrtapproximationapproximationsqrt

double computeSquareRootBabylonian
(double num, 
 double initialApproximation,
 unsigned int iterations)
{
    double approximation = initialApproximation;
    unsigned int iteration = 1;

    for (unsigned int iterationsRemaining = iterations; 
         iterationsRemaining > 0; 
         iterationsRemaining--) 
    {
        approximation =(approximation + (num/approximation))/2;
        cout << endl
             << "Cycle " << iteration << " Aproximate: " << approximation << endl;

        iteration++;
    }
    return approximation;
}

5.在每次迭代中记录逐次近似值并不是您总是希望平方根函数执行的那种事情。我们应该添加一个参数,允许调用代码指定是否记录近似值:

double computeSquareRootBabylonian
(double num, 
 double initialApproximation,
 unsigned int iterations,
 bool log)
{
    double approximation = initialApproximation;
    unsigned int iteration = 1;

    for (unsigned int iterationsRemaining = iterations; 
         iterationsRemaining > 0; 
         iterationsRemaining--) 
    {
        approximation =(approximation + (num/approximation))/2;
        if (log) 
        {
            cout << endl << "Cycle " << iteration << " Aproximate: " << approximation << endl;
        }
        iteration++;
    }
    return approximation;
}

编写 main 函数

现在,我们已经完成了程序的所有组成部分。我们只需要改变我们的计划

show a greeting
do {
    get number from user
    compute the squareroot of that number while logging
    show the user the squareroot
    ask the user if they want to do another computation
} while the user wants the program to keep running
show a farewell

变成一个完整的程序:

int main(int argc, char *argv[]) 
{
    //Do this stuff.
}

首先,我们知道如何开始和结束程序。我们想用我们的函数来表达问候和告别,并且:showGreeting()showFarewell()

int main(int argc, char *argv[]) 
{
    showGreeting();
    //do {
        //get number from user
        //compute the squareroot of that number while logging
        //show the user the squareroot
        //ask the user if they want to do another computation
    //} while the user wants the program to keep running
    showFarewell();
}

我们知道我们想要获取用户输入并至少计算一次平方根,并且我们知道我们可以得到一个表示用户是否想使用我们的函数计算另一个平方根。我们应该对用户进行输入和计算!boolgetRunAgain()dowhilegetRunAgain()

int main(int argc, char *argv[]) 
{
    showGreeting();
    do {
        //get number from user
        //compute the squareroot of that number while logging
        //show the user the squareroot
    } while(getRunAgain());
    showFarewell();
}

我们有一个函数,它返回一个表示用户想要计算其平方根的数字。我们需要使用它两次,一次作为平方根函数的参数,一次将输入及其平方根输出给用户。因此,我们需要一个局部变量:getInputNumber()doubledoublecomputeSquareRootBabylonian()

int main(int argc, char *argv[]) 
{
    showGreeting();
    do {
        double number = getInputNumber();
        //compute the squareroot of that number while logging
        //show the user the squareroot
    } while(getRunAgain());
    showFarewell();
}

现在我们的平方根函数有四个参数:computeSquareRootBabylonian()

  • 要计算平方根的数目
  • 初始近似值
  • 迭代次数
  • 是否记录每个连续近似值

我们将作为第一个参数传递。现在,我们可以对剩余的参数进行硬编码,用于初始近似值、迭代次数以及是否记录。number125true

由于我们只需要使用一次的结果(在注销结果时),我们可以在不使用局部变量的情况下离开。不过,为了清楚起见,让我们继续使用一个:computeSquareRootBabylonian()

int main(int argc, char *argv[]) 
{
    showGreeting();
    do {
        double number = getInputNumber();
        double squareRoot = computeSquareRootBabylonian(number, 1.0f, 25, true);
        //show the user the squareroot
    } while(getRunAgain());
    showFarewell();
}

我们只需要向用户显示结果:

int main(int argc, char *argv[]) 
{
    showGreeting();
    do {
        double number = getInputNumber();
        double squareRoot = computeSquareRootBabylonian(number, 1.0f, 25, true);
        cout << endl
             << "The square root of " << num << " is " << sqrt << "!" << endl
             << endl;
    } while(getRunAgain());
    showFarewell();
}

此程序遗漏了一件事:配置 的精度。cout

int main(int argc, char *argv[]) 
{
    cout.precision(50);
    showGreeting();
    do {
        double number = getInputNumber();
        double squareRoot = computeSquareRootBabylonian(number, 1.0f, 25, true);
        cout << endl
             << "The square root of " << num << " is " << sqrt << "!" << endl
             << endl;
    } while(getRunAgain());
    showFarewell();
}

重述问题

您问了几个问题:cout

在此代码 [第一个函数] 中,我专门将小数点的精度设置为 15。但是,它以 12 位小数输出数字。但是在这个代码中[第二个函数],它以小数点后 15 位输出。[...]我对为什么会发生这种情况感到非常困惑。我尝试了 cout << 固定<<展示点;但是当 NUM 无缘无故输出 15 个零时,它看起来很不愉快。谁能告诉我为什么会发生这种情况以及如何解决它?mainmain

为什么我不能超过小数点后 15 位?如果我将精度设置为 30/40,即使在我的计算机计算器中它变为 30/40,它也不会对我的输出产生任何影响。我应该使用另一种整数类型来代替双精度类型吗?

我稍后会回答这些问题。首先,我将描述我如何亲自询问他们,以便在 StackOverflow 上获得更好、更快的响应。

重点是发布一个简短的、独立的、正确的(可编译的)示例。那么这里到底有什么问题呢?它与在 std::cout(通常使用 std::ostream)中使用插入(使用运算符 <<)有关。它与使用 std::ostream::p recision 格式化输出有关。在将问题简化到这一点之后,我将开始查看相关文档,如果这不能提供足够的解释,请使用这些关键字在 StackOverflow 上搜索。如果我找不到问题的解决方案,我会生成一个示例来说明我遇到的困难。double

提出第一个问题

第一个问题是,当我们将 cout 的精度设置为 15 时,我们只得到 12 位小数。让我们编写一个程序,将 a 初始化为一个值,设置 to 的精度并将我们的 into 插入:doublecout15doublecout

#include <iostream>
using namespace std;

int main(int argc, char *argv[]) 
{
    double value = 1.1111111111111111111111111f;
    cout.precision(15);
    cout << value << endl;
    return 0;
}

该程序打印出来

1.11111111111111

这个数字在小数点后只有 14 个。虽然这并不完全是我们所希望的(小数点后有 14 位而不是 12 位),但看起来我们走在正确的轨道上。看起来某种舍入行为正在发生。也许我们应该在小数点后第十三位和第十四位加零:1

#include <iostream>
using namespace std;

int main(int argc, char *argv[]) 
{
    double value = 1.1111111111110011111111111f;
    cout.precision(15);
    cout << value << endl;
    return 0;
}

该程序的输出

1.111111111111

只有 12 位数字,正如我们所希望的那样。因此,我们现在有一个简短的、独立的、正确的(可编译的)示例来放在我们的问题中。

陈述第一个问题

我用来输出一个.我想显示小数点后的前 15 位数字,或者至少显示非零数字。但是在此代码中,例如std::coutdouble

int main(int argc, char *argv[]) 
{
    double value = 1.1111111111110011111111111f;
    cout.precision(15);
    cout << value << endl;
    return 0;
}

输出与我预期的不同。我能够通过使用来解决这个问题1.1111111111111.111111111111001std::fixed

int main(int argc, char *argv[]) 
{
    double value = 1.1111111111110011111111111f;
    cout.precision(15);
    cout << std::fixed << value << endl;
    return 0;
}

它按预期输出。不幸的是,使用会导致始终在小数点后打印 15 位数字,即使所有这些数字都是零。例如,输出1.111111111111001std::fixed

int main(int argc, char *argv[]) 
{
    double value = 1.0f;
    cout.precision(15);
    cout << std::fixed << value << endl;
    return 0;
}

而不是我所希望的那样。1.0000000000000001

当所有剩余数字均为零时,如何输出最多 15 个十进制数字或更少?cout

提出第二个问题

第二个问题是,无论我们设置的精度比 15 高多少,我们的输出中都只能得到 15 位十进制数字。让我们编写一个程序,将 a 初始化为一个值,设置 to 的精度并将我们的 into 插入:coutdoublecout30doublecout

int main(int argc, char *argv[]) 
{
    double value = 1.55f;
    cout.precision(30);
    cout << value << endl;
    return 0;
}

这输出 ,一个由小数点后 22 位数字组成的数字。这表明我们不知何故不幸地得到了正好 15 位的输出:我们在提出第一个问题时遇到的相同类型的舍入行为显然是与平方根函数返回的值的输出进行交互,因此在这种舍入行为下,小数位后 15 位被打印出来。1.5499999523162841796875


回答问题

您希望插入 a,以便显示尽可能多的非零十进制数字,最多 15 位。s in 的字符串表示形式应至少满足以下测试:doublecoutdoublecout

 x.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxf ----> "x.xxxxxxxxxxxxxxx"
 x.xxxxx0000000000000000000000000f ----> "x.xxxxx"
 x.000000000000000000000000000000f ----> "x"
 x.00000000000000x000000000000000f ----> "x.00000000000000x"

这不能通过使用 以任何明显的方式实现。正如您指出的,我们不能使用,因为这肯定会使我们上面的第三个测试失败。我们也不能轻易使用默认值,因为如 cplusplus.com 所述,std::ostream::precisionstd::fixed

在默认浮点表示法中,精度字段指定在小数点之前和之后的计数中要显示的最大有意义数字数。

这意味着我们需要考虑小数点后几位之前的位数。如果小数点前有 3 位数字,我们需要使用 15+3 的精度,依此类推。

实现此目的的一种方法是函数,该函数接受 a 并返回:doublestd::string

std::string stringFromDouble(double value, unsigned int decimalDigits)
{
    int integerComponent = abs((value > 0) ? (int)(floor(value)) : (int)(ceil(value)));
    std::ostringstream integerStream;
    integerStream << integerComponent;
    std::string integerString = integerStream.str();
    unsigned int integerLength = integerString.length();

    std::ostringstream stream;
    stream.precision(decimalDigits + integerLength);
    stream << value;
    std::string str = stream.str();
    return str;
}

评论

2赞 sehe 1/14/2013
等等,哇?!你批评这个问题,说它应该如何清晰易读。然后你把任何残余的答案埋在一层厚厚的规范之下......打趣?TL;DR来自我
2赞 Andriy Tylychko 1/14/2013
哇,答案的大小给我留下了深刻的印象