如何输出double值正确的小数位数?

28

我需要帮助保持 double 的精度。如果我将一个字面量赋给一个 double,实际值会被截断。

int main() {
    double x = 7.40200133400;
    std::cout << x << "\n";
}

对于上面的代码片段,输出结果为7.402
有没有办法防止这种截断?或者有没有一种方法可以准确地计算一个double的浮点数?例如,number_of_decimal(x)会给出11,因为输入在运行时是未知的,所以我不能使用setprecision()
我认为我应该改变我的问题: 如何将一个double转换为字符串而不截断浮点数。 即:
#include <iostream>
#include <string>
#include <sstream>

template<typename T>
std::string type_to_string( T data ) {
    std::ostringstream o;
    o << data;
    return o.str();
}

int main() {
    double x = 7.40200;
    std::cout << type_to_string( x ) << "\n";
}

期望的输出应该是7.40200,但实际结果是7.402。那么我该如何解决这个问题呢?有什么想法吗?


请参考此链接了解浮点数精度。 - Benoit
1
"预期输出" - 问题出在你的期望上。 "7.40200但实际结果是7.402" - 这两个数是相等的,所以没有被“截断”。cout不可能知道你在源文件中输入了多少个零。 - Jim Balter
9个回答

34

由于浮点数和双精度浮点数在内部是以二进制形式存储的,因此字面量7.40200133400实际上代表数字7.40200133400000037653398976544849574565887451171875。

那么你真正想要多少精度呢?:-)

#include <iomanip>    
int main()
{
    double x = 7.40200133400;
    std::cout << std::setprecision(51) << x << "\n";
}

是的,这个程序确实会打印出7.40200133400000037653398976544849574565887451171875!


我想要7.40200133400,不多不少。 - roxrook
2
@Chan:为什么?因为你在源文件中写了 7.40200133400 吗?这是不可能的,因为对于编译器来说,字面量 7.4020013347.402001334007.402001334000000 是等价的。它们都产生相同的值,然后存储在 x 中。关于你在末尾写下了多少个零,没有额外的信息。 - fredoverflow
@Fred:嗯,这比我最初想象的要复杂得多。就像你说的那样,这是不可能做到的吗? - roxrook
3
如果你只是想从文本文件中读取一个数字并打印它(而不进行任何计算),那么为什么不将其作为std::string读取,而不是double呢? - fredoverflow
C++ 中的 double 类型有 15-17 位小数float 类型则只有一半(约 7 位)。 - Tomas
显示剩余4条评论

20

您必须使用setiosflags(ios::fixed)setprecision(x)

例如,cout << setiosflags(ios::fixed) << setprecision(4) << myNumber << endl;

此外,不要忘记#include <iomanip.h>


2
+1 for ios::fixed 如下: setiosflags(ios::fixed) 有助于当num == (int)num的情况,允许以2.00这样的格式输出数字,否则输出将为2。 - spin_eight

11
std::cout << std::setprecision(8) << x;

请注意,setprecision是持久的,之后打印的所有浮点数都将使用该精度进行打印,直到您将其更改为不同的值。如果这是一个问题,并且您想解决它,您可以使用代理 stringstream 对象:

std::stringstream s;
s << std::setprecision(8) << x;
std::cout << s.str();

如果想了解有关iostream格式化的更多信息,请查看cppreference中的输入/输出操作符部分。


嗨Kos,我理解你的意思,但问题在于精度从一个值变成另一个值。例如:1.233和3.99945,一个有3位小数,另一个有5位小数。 - roxrook
我的错!浮点数不同,而不是精度。 - roxrook

5

使用Boost.Format的解决方案:

#include <boost/format.hpp>
#include <iostream>

int main() {
    double x = 7.40200133400;
    std::cout << boost::format("%1$.16f") % x << "\n";
}

这将输出7.4020013340000004
希望这可以帮助你!

首先,感谢Daniel。但我认为boost::format的行为与cout.set(0, std :: ios :: floatfield)完全相同,而您的输出不是原始输出。我的问题是我想将该double值转换为字符串。 - roxrook
1
原始的代码只是你在文本文件中输入的内容,它并不一定代表实际的值。 - Daniel Lidström

3

我想到的唯一答案是:没有正确的方法来计算小数位数!这主要原因是数字的表示可能不是你期望的,例如,128.82 看起来很无害,但它的实际表示是 128.8199999999... 那么你如何计算小数位数呢?


1
你可以添加或删除0.0000000001,然后再次打印它,看看新的输出是否使用更少的字符。添加或删除的数量取决于您所需的精度。 - Daniel Lidström
1
除非您真的担心那种精度级别,否则您只需四舍五入到小数点后15位。在15位之后,您的精度也不是很好。 - Edward Strange
实际上,我想将这个值转换为字符串,并且我希望保留它从文件中读取时的格式。 - roxrook
2
你的变量以二进制形式存储。十进制有限表示数在二进制中可能具有非有限表示。如果且仅当您的数字是2的幂次和时,表示才是有限的。 - Benoit

2

回答你的编辑答案:没有办法做到这一点。一旦您给double赋值,任何尾随的零都会丢失(对于编译器/计算机来说,0.402、0.4020和0.40200是相同的数字)。唯一保留尾随零的方法是将值存储为字符串(或者进行技巧处理,跟踪您关心的数字数量并将其格式化为确切的长度)。


1
让我们举一个类似的例子:在将整数初始化为001后,您希望以前导零打印它。但是,该格式化信息根本没有被存储。
要进一步了解双精度浮点存储,请查看IEEE 754标准。

1
双精度浮点数没有小数位,它们有二进制位。而二进制位和十进制位是不可比的(因为log2(10)不是整数)。
你所要求的并不存在。

1
这道题的第二部分是关于如何在从值规范到输出结果的过程中保留浮点值中的尾随零,这是无解的。浮点值不会保留原始的值规范。似乎这个荒谬的部分是由一个SO论坛的管理员添加的。
关于问题的第一部分和最初部分,我的理解是如何呈现7.40200133400的所有有效数字,即输出结果应该类似于7.402001334,你可以从只包含double值中可信赖数字的输出结果中删除尾随零。
#include <assert.h>         // assert
#include <limits>           // std::(numeric_limits)
#include <string>           // std::(string)
#include <sstream>          // std::(ostringstream)

namespace my{
    // Visual C++2017 doesn't support comma-separated list for `using`:
    using std::fixed; using std::numeric_limits; using std::string;
    using std::ostringstream;

    auto max_fractional_digits_for_positive( double value )
        -> int
    {
        int result = numeric_limits<double>::digits10 - 1;
        while( value < 1 ) { ++result; value *= 10; }
        return result;
    }

    auto string_from_positive( double const value )
        -> string
    {
        ostringstream stream;
        stream << fixed;
        stream.precision( max_fractional_digits_for_positive( value ) );
        stream << value;
        string result = stream.str();
        while( result.back() == '0' )
        {
            result.resize( result.size() - 1 );
        }
        return result;
    }

    auto string_from( double const value )
        -> string
    {
        return (0?""
            : value == 0?   "0"
            : value < 0?    "-" + string_from_positive( -value )
            :               string_from_positive( value )
            );
    }
}

#include<iostream>
auto main()
    -> int
{
    using std::cout;
    cout << my::string_from( 7.40200133400 ) << "\n";
    cout << my::string_from( 0.00000000000740200133400 ) << "\n";
    cout << my::string_from( 128.82 ) << "\n";
}

输出:

7.402001334
0.000000000007402001334
128.81999999999999
你可以考虑添加四舍五入的逻辑,以避免出现类似最后一个结果中的长序列的9。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接