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

How to parse and split a double in c++

本文关键字:拆分 双精度 c++      更新时间:2023-10-16

我有三个数字:

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

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

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

如果你的数字最初出现在数据类型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,或者通常要找出源代码中编写的数字文本的小数部分,则必须首先将它们存储为字符串,而不是双精度

我猜你不熟悉"浮点"数字到底是什么。

双精度数是浮点数,如 https://en.wikipedia.org/wiki/IEEE_floating_point 中所述。基本上1.101.1一样,和1.1000000一样,以此类推。

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

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