如何在 C++ 中解析和拆分双精度数

How to parse and split a double in c++

我有这三个号码:

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

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

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

我读到使用 std::to_string 是不可能的,因为它总是 returns 所有小数,例如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 时,它 returns 1 而不是 10

我正在考虑如何解决这个问题,唯一的办法就是在转换为字符串之前计算精度,然后重新设置精度。

这是一个可行的策略吗? 我如何计算点后有多少位数字?

我猜你不熟悉 'floating point' 数字到底是什么。

double 是 https://en.wikipedia.org/wiki/IEEE_floating_point 中描述的浮点数。基本上1.101.1是一样的,跟1.1000000是一样的等等。

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

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

如果您的数字最初出现在数据类型 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,或者你一般想找出你源代码中写的数字文字的小数部分,那么你必须存储它们最初作为字符串,而不是双打。