双印不失精度
如何将 double 打印到流中,以便在读入时不会丢失精度?
How do you print a double to a stream so that when it is read in you don't lose precision?
我试过了:
std::stringstream ss;
double v = 0.1 * 0.1;
ss << std::setprecision(std::numeric_limits<T>::digits10) << v << " ";
double u;
ss >> u;
std::cout << "precision " << ((u == v) ? "retained" : "lost") << std::endl;
这并没有像我预期的那样工作.
This did not work as I expected.
但我可以提高精度(这让我感到惊讶,因为我认为digits10 是所需的最大值).
But I can increase precision (which surprised me as I thought that digits10 was the maximum required).
ss << std::setprecision(std::numeric_limits<T>::digits10 + 2) << v << " ";
// ^^^^^^ +2
这与有效位数有关,前两位不计入(0.01).
It has to do with the number of significant digits and the first two don't count in (0.01).
那么有没有人研究过精确地表示浮点数?我需要在流中执行的确切魔法咒语是什么?
So has anybody looked at representing floating point numbers exactly? What is the exact magical incantation on the stream I need to do?
经过一些实验:
问题在于我的原始版本.字符串中小数点后有非有效数字影响准确性.
The trouble was with my original version. There were non-significant digits in the string after the decimal point that affected the accuracy.
所以为了弥补这一点,我们可以用科学记数法来弥补:
So to compensate for this we can use scientific notation to compensate:
ss << std::scientific
<< std::setprecision(std::numeric_limits<double>::digits10 + 1)
<< v;
这仍然不能解释 +1 的必要性.
This still does not explain the need for the +1 though.
另外,如果我打印出更精确的数字,我会得到更高的打印精度!
Also if I print out the number with more precision I get more precision printed out!
std::cout << std::scientific << std::setprecision(std::numeric_limits<double>::digits10) << v << "
";
std::cout << std::scientific << std::setprecision(std::numeric_limits<double>::digits10 + 1) << v << "
";
std::cout << std::scientific << std::setprecision(std::numeric_limits<double>::digits) << v << "
";
结果:
1.000000000000000e-02
1.0000000000000002e-02
1.00000000000000019428902930940239457413554200000000000e-02
基于以下@Stephen Canon 的回答:
Based on @Stephen Canon answer below:
我们可以使用 printf() 格式化程序%a"或%A"准确地打印出来.为了在 C++ 中实现这一点,我们需要使用固定的和科学的操纵器(参见 n3225:22.4.2.2.2p5 表 88)
We can print out exactly by using the printf() formatter, "%a" or "%A". To achieve this in C++ we need to use the fixed and scientific manipulators (see n3225: 22.4.2.2.2p5 Table 88)
std::cout.flags(std::ios_base::fixed | std::ios_base::scientific);
std::cout << v;
现在我已经定义了:
template<typename T>
std::ostream& precise(std::ostream& stream)
{
std::cout.flags(std::ios_base::fixed | std::ios_base::scientific);
return stream;
}
std::ostream& preciselngd(std::ostream& stream){ return precise<long double>(stream);}
std::ostream& precisedbl(std::ostream& stream) { return precise<double>(stream);}
std::ostream& preciseflt(std::ostream& stream) { return precise<float>(stream);}
下一篇:我们如何处理 NaN/Inf?
Next: How do we handle NaN/Inf?
推荐答案
如果您不想丢失精度,请不要以十进制打印浮点值.即使您打印了足够多的数字来准确表示该数字,也不是所有实现都在整个浮点范围内正确舍入到/从十进制字符串转换,因此您仍然可能会丢失精度.
Don't print floating-point values in decimal if you don't want to lose precision. Even if you print enough digits to represent the number exactly, not all implementations have correctly-rounded conversions to/from decimal strings over the entire floating-point range, so you may still lose precision.
改用十六进制浮点数.在 C 中:
Use hexadecimal floating point instead. In C:
printf("%a
", yourNumber);
C++0x 为执行相同操作的 iostream 提供 hexfloat
操纵器(在某些平台上,使用 std::hex
修饰符具有相同的结果,但这不是一个可移植的假设).
C++0x provides the hexfloat
manipulator for iostreams that does the same thing (on some platforms, using the std::hex
modifier has the same result, but this is not a portable assumption).
出于多种原因,首选使用十六进制浮点数.
Using hex floating point is preferred for several reasons.
首先,打印的值总是准确的.在写入或读取以这种方式格式化的值时不会发生舍入.除了准确性优势之外,这意味着使用经过良好调整的 I/O 库可以更快地读取和写入此类值.它们还需要更少的数字来准确表示值.
First, the printed value is always exact. No rounding occurs in writing or reading a value formatted in this way. Beyond the accuracy benefits, this means that reading and writing such values can be faster with a well tuned I/O library. They also require fewer digits to represent values exactly.
相关文章