在C ++中将双精度数字渲染为字符串的最佳方法是什么?
我遇到了这篇文章http://www.serpentine.com/blog/2011/06/29/here-be-dragons-advances-in-problems-you-didnt-even-know-you-had/,其中有关于打印浮动点的信息?
我一直在使用sprintf_s。我不明白为什么我需要修改代码?
答案 0 :(得分:3)
啊哈!
你给出的文章中提到的问题是,对于某些数字,计算机显示的是理论上正确的东西,但不是我们人类会使用的东西。
例如,就像文章所说的那样,1.2999999 ... = 1.3,所以如果你的结果是1.3,那么(非常)正确的计算机将其显示为1.299999999 ...但这不是你所看到的。 ..
现在问题是计算机为什么这样做?原因是计算机在基数2(二进制)中计算,我们通常以10(十进制)计算。结果是一样的(感谢上帝!),但内部存储和表示不是。
当在基数10中显示时,有些数字看起来不错,例如1.3,但是其他数字没有,例如1/3 = 0.333333333 ....它在基数2中是相同的,有些数字“看起来”在基数上很好2(通常由2的分数组成)和其他没有。当计算机在内部存储数字时,它可能无法“准确地”存储它并存储最接近的可能表示,即使该数字在十进制中看起来是“有限的”。所以是的,在这种情况下,它会“漂移”一点点。如果你一次又一次地这样做,你可能会失去精确度。但没有其他办法(除非使用能够存储分数的特殊数学库)
当计算机试图以10给你的数量给你回到基数10时出现问题。然后计算机可能会给你1.299999而不是你预期的1.3。
这也是你永远比较浮点数与==,<,>的原因,而是使用特殊函数 islessgreater(a,b)isgreater(a,b) )等
所以你使用的实际函数(sprintf)很好并且尽可能准确,它给你正确的值,你只需要知道当处理浮点数时,如果你期望1.3 <1.3999999,那么最大精度是可以的/ p>
现在,如果你想“漂亮地打印”这些数字以获得最佳“人类”表示(基数为10),你可能想要使用一个特殊的库,就像你的grisu3一样,它会试图消除可能发生的漂移并将数字与最近的基数10表示对齐。
现在图书馆不能使用水晶球并找到漂移的数字,所以可能会发生你真的意味着1.2999999以最大精度存储在计算机中并且lib将“转换“它到1.3 ......但它并不比显示1.29999而不是1.3更精确,也不精确。
如果您需要良好的可读性,那么这样的lib将非常有用。如果没有,那只是浪费时间。
希望这有帮助!
答案 1 :(得分:3)
如果您对sprintf_s感到满意,则不应更改。但是,如果您需要以库不支持的方式格式化输出,则可能需要重新实现sprintf的专用版本(使用任何已知算法)。
例如,JavaScript对如何打印数字有非常精确的要求(参见specification的第9.8.1节)。只需调用sprintf就无法完成正确的输出。实际上,Grisu已经开发用于为JavaScript编译器实现正确的数字打印。
Grisu也比sprintf快,但除非浮点打印是您应用程序的瓶颈,否则这不应该是切换到不同库的理由。
答案 2 :(得分:1)
在C ++中你为什么不使用iostreams?您可能应该使用cout
作为控制台,使用ostringstream
作为面向字符串的输出(除非您非常特别需要使用printf
系列方法)。
除非实际分析显示CPU是瓶颈(与I / O相比),否则您不必担心格式化性能。
答案 3 :(得分:1)
在任何合理语言中执行此操作的最佳方法是:
我并不是要劝阻你或任何人。这些实际上是令人着迷的功能,但它们也很复杂,并且试图为任何非天真的实现设计良好的测试覆盖率甚至更多。除非你准备好花几个月思考这个问题,否则不要开始。
答案 4 :(得分:0)
您可能要使用Grisu(或faster method)之类的东西,因为它可以为您提供具有往返保证的最短十进制表示形式,而sprintf
则只需要固定的精度。好消息是C ++ 20包含std::format
,默认情况下会为您提供此功能。例如:
printf("%.*g", std::numeric_limits<double>::max_digits10, 0.3);
同时打印0.29999999999999999
puts(fmt::format("{}", 0.3).c_str());
打印0.3
(godbolt)。
在此期间,您可以使用the {fmt} library,std::format
是基于。 {fmt}还提供了print
功能,使此操作变得更加轻松和高效(godbolt):
fmt::print("{}", 0.3);
免责声明:我是{fmt}和C ++ 20 std::format
的作者。
答案 5 :(得分:-1)
void outputdouble( ostringstream & oss, double d )
{
oss.precision( 5 );
oss << d;
}