如何'cout'双值的正确小数位数?

时间:2010-11-18 17:02:38

标签: c++ double tostring precision

我需要帮助保持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。那么我该如何解决这个问题呢?有什么想法吗?

9 个答案:

答案 0 :(得分:28)

由于floatdouble内部存储在二进制文件中,文字7.40200133400实际上代表数字7.40200133400000037653398976544849574565887451171875

...你真的想要多少精确度? : - )

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

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

答案 1 :(得分:18)

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

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

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

答案 2 :(得分:9)

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

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

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

有关iostream格式的更多信息,请查看cppreference中的Input/output manipulators部分。

答案 3 :(得分: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

希望这有帮助!

答案 4 :(得分:3)

我提出的唯一答案就是没有办法正确执行此操作(如计算小数位)!主要原因是数字的表示可能不是你所期望的,例如128.82,看起来似乎无害,但它的实际表示是128.8199999999 ...你怎么计算那里的小数位数?< / p>

答案 5 :(得分:2)

回应你的答案 - 编辑:没有办法做到这一点。一旦为double分配值,任何尾随零都会丢失(对于编译器/计算机,0.402,0.4020和0.40200是相同的号码)。如您所示,保留尾随零的唯一方法是将值存储为字符串(或者在您跟踪您关注的位数并将其格式化为该长度的位置时进行欺骗)。

答案 6 :(得分:2)

双打没有小数位。他们有二进制位置。二进制位和小数位是不可通约的(因为log2(10)不是整数)。

你要求的东西不存在。

答案 7 :(得分:1)

让我们做一个类似的请求:在用001初始化一个整数之后,你会想要用前导零打印它。格式化信息从未存储过。

要进一步了解双精度浮点存储,请查看IEEE 754标准。

答案 8 :(得分:0)

问题的第二部分,关于如何在从值规范到输出结果的浮点值中保留尾随零,没有解决方案。浮点值不保留原始值规范。似乎这个荒谬的部分是由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的长序列,就像在上一个结果中一样。