【问题标题】:How to 'cout' the correct number of decimal places of a double value?如何“计算”双精度值的正确小数位数?
【发布时间】:2015-07-29 20:57:30
【问题描述】:

我需要帮助来保持double 的精度。如果我将文字分配给双精度,则实际值会被截断。

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

对于上面的代码sn-p,输出为7.402
有没有办法防止这种类型的截断?或者有没有办法准确计算double 的浮点数?例如,number_of_decimal(x) 会给出 11,因为输入在运行时是未知的,所以我不能使用 setprecision()


我想我应该把我的问题改成: 如何在不截断浮点数的情况下将双精度数转换为字符串。即

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

【问题讨论】:

  • 浮点精度见this
  • “预期的输出”——问题出在你的预期上。 “7.40200 但实际结果是 7.402”——它们是相等的,所以没有任何东西被“截断”。 cout 不可能知道您在源文件中输入了多少个零。

标签: c++ double tostring precision


【解决方案1】:

由于floatdouble 在内部以二进制形式存储,因此文字7.40200133400 实际上代表数字7.40200133400000037653398976544849574565887451171875

...那么您真正想要多少精度? :-)

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

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

【讨论】:

  • 嗨 Fred,我不想要任何指定的精度。我只想将其转换为字符串作为其原始格式。如果我误解了您的指示,请纠正我。谢谢,
  • @Chan:你想要7.402001334还是7.40200133400还是7.402001334000000
  • 我要 7.40200133400,不多不少。
  • @Chan:为什么?因为你在源文件中写了7.40200133400?这是不可能的,因为对于编译器来说,7.4020013347.402001334007.402001334000000 是等价的。它们都产生相同的值,然后存储在x 中。没有关于你最后写了多少个零的额外信息。
  • @Chan:如果你只想从文本文件中读取一个数字然后打印它(不做任何计算),你为什么不把它读成std::string 而不是double?
【解决方案2】:

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

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

另外,别忘了#include &lt;iomanip.h&gt;

【讨论】:

  • +1 for ios::fixed as: setiosflags(ios::fixed) 有助于解决 num == (int)num 的情况,允许以 2.00 之类的格式输出数字,没有它的输出将是 2
【解决方案3】:
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 部分。

【讨论】:

  • 嗨,Kos,我明白你的意思,但问题是精度从一个值变为另一个值。例如:1.233 和 3.99945,一个有 3,一个有 5。
  • 我的错!浮点数不同而不是精度。
【解决方案4】:

使用Boost.Format的解决方案:

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

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

这会输出7.4020013340000004

希望这会有所帮助!

【讨论】:

  • 首先,谢谢丹尼尔。但我认为 boost::format 的行为与 cout.set( 0, std::ios::floatfield ) 完全相同,并且您的输出不是原始输出。我的问题是我想将该双精度值转换为字符串。
  • 原始文件只是您在文本文件中键入的内容。不一定代表实际值。
【解决方案5】:

我想出的唯一答案是没有办法正确地做到这一点(如计算小数位)!主要原因是一个数字的表示可能不是你所期望的,例如 128.82,看起来足够无害,但它的实际表示是 128.8199999999......你如何计算那里的小数位数??

【讨论】:

  • 您可以在其中添加或删除 0.0000000001,然后再次打印,看看新输出是否使用了较少的字符。添加或删除的数量取决于您满意的精度。
  • 除非您真的担心精度水平,在这种情况下您可能会自己做事,您只需四舍五入到 15 个位置。无论如何,15 岁之后你的精度就不高了。
  • 其实我想把这个值转换成一个字符串,我想保持它从文件中读取的格式。
  • 您的变量以二进制形式存储。十进制有限表示数字可能具有二进制的非有限表示。当且仅当您的数字是 2 的幂和时,表示是有限的。
【解决方案6】:

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

【讨论】:

    【解决方案7】:

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

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

    【讨论】:

      【解决方案8】:

      双精度数没有 个小数位。他们有二进制的地方。而且二进制位和小数位是不可通约的(因为log2(10) 不是整数)。

      您所要求的不存在。

      【讨论】:

        【解决方案9】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-08-16
          相关资源
          最近更新 更多