【问题标题】:How to parse and split a double in c++如何在 C++ 中解析和拆分双精度
【发布时间】:2017-09-10 11:50:08
【问题描述】:

我有这三个数字:

double n1 = 1.1
double n2 = 1.10
double n3 = 1.101

我希望将它们转换为字符串以分隔小数部分以在其他地方使用它们。因此,我想要以下内容:

string s1 = 1
string s2 = 10
string s3 = 101

我读到使用std::to_string 是不可能的,因为它总是返回所有小数,例如s3 类似于 101000

然后我尝试了以下方法:

double n3 = 1.101;
std::ostringstream strs;
strs << n3;
std::string str = strs.str();

boost::char_separator<char> sep(".");
boost::tokenizer<boost::char_separator<char>> tokens(str,sep);

for (const auto& t : tokens) {
    EV << "separated parts " << t << endl;
}

这在n1n3 情况下工作正常,但是当我使用n2 时,它返回1 而不是10

我正在考虑如何解决这个问题,唯一的事情是在转换为字符串之前计算精度,然后再次设置精度。

这是一个可行的策略吗? 如何计算点后的位数?

【问题讨论】:

  • 1.1 == 1.10,没有区别。
  • 简单的初等数学,小数部分末尾有多少个零并不重要。所以,1.1 == 1.10,还有 1.10 == 1.100000000000000000...
  • 为什么不使用std::modf()
  • 我知道,但这是给我的代码。 1.1 的含义与 1.10 不同。如果输入的是1.1我希望能够和1.10区分开来
  • @brid 为什么最初需要双精度值?

标签: c++ string type-conversion double precision


【解决方案1】:

我猜你不熟悉“浮点”数实际上是什么。

double 是一个浮点数,如 https://en.wikipedia.org/wiki/IEEE_floating_point 中所述。基本上1.101.1相同,与1.1000000相同等等。

现在,有一种称为“定点数”的解决方案,通常称为“小数”和“数字”。如果您熟悉数据库引擎,就会知道 1.10 是 numeric(3,2) 而 1.1 是 numeric(2,1)。请注意类型如何更改并定义精度(位数)和小数位数(逗号后的位数)。但是,C++ 本身并不支持小数。

【讨论】:

  • 感谢您的回复,但我不明白这对我有何帮助。你是说我想做的事是不可能的吗?
  • @brid,double 不存储有关数字位数的信息。所以不,使用简单的双精度是不可能的。您当然可以将其包装在自定义数据结构中并在解析时添加该信息,或者使用定点库。
【解决方案2】:

这只是简单的数学运算。 解析器只是将您的 x.10 转换为 x.1 :)

【讨论】:

    【解决方案3】:

    如果您的数字最初出现在数据类型double 中,那么我认为没有机会将小数部分分开,如用于初始化的文字中所写。由于我将在以下段落中尝试解释,因此这些信息完全丢失了:

    注意文字1.101.1 都代表double 类型的数字文字。编译器会将两种文字转换为浮点表示/二进制表示,1.101.1 将实现完全相同的二进制表示。因此,您将没有任何机会查明双精度值是否已使用文字 1.11.10 进行初始化。

    当我们考虑到十进制数 1.1 没有精确的二进制表示但会导致重复分数时,情况会变得更糟:

    0.1₁₀ = 0.0001100110011001100110011001100110011001100110011…₂
    

    因此,当您打印出文字 1.1(或 1.10,相同)时,这取决于您要舍入的精度:

    std::cout << "1.1 is..." << std::setprecision(6) << 1.1 << std::endl;
    std::cout << "1.1 is..." << std::setprecision(17) << 1.1  << std::endl;
    // 1.1 is...1.1
    // 1.1 is...1.1000000000000001
    

    这意味着 - 一旦存储为双精度值,您会得到不同的结果,具体取决于您用于打印该值的精度;这也适用于将小数部分转换为字符串,这实际上与以特定精度打印相同。

    更糟糕的是,由于无法准确地以二进制形式表示每个十进制值,两个不同的十进制值可以产生相同的二进制值:

    double n1 = 1.1;
    double n2 = 1.100000000000000088817841970012523233890533447265625;
    
    if (n1 == n2)
        std::cout << "equal!" << std::endl;
    // Output: equal!
    

    所以你不能再区分n1n2了。

    很多话,一个简短的结论:

    如果您想将1.11.10 区分开来,或者您通常想找出源代码中写入的数字文字的小数部分,那么您必须最初存储它们 作为字符串,而不是双精度数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-06-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-13
      • 2012-01-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多