dtoa vs sprintf vs Grisu3 algorithm
dtoa vs sprintf vs Grisu3 algorithm
C++中将双精度数字呈现为字符串的最佳方法是什么?
我看到了这篇文章 这里是龙:你甚至不知道你的问题的进展 讨论打印浮点数。
我一直在使用sprintf
.我不明白为什么我需要修改代码?
对sprintf_s感到满意,你就不应该改变。但是,如果您需要以库不支持的方式格式化输出,则可能需要重新实现专用版本的 sprintf(使用任何已知算法(。
例如,JavaScript 对其数字必须如何打印有非常精确的要求(参见规范的第 9.8.1 节(。正确的输出不能通过简单地调用 sprintf 来完成。事实上,Grisu 的开发是为了为 JavaScript 编译器实现正确的数字打印。
Grisu 也比 sprintf 快,但除非浮点打印是应用程序中的瓶颈,否则这不应该成为切换到其他库的理由。
啊!
你给出的文章中概述的问题是,对于某些数字,计算机显示的东西在理论上是正确的,但不是我们人类会使用的东西。
例如,就像文章所说的,1.2999999... = 1.3,所以如果你的结果是1.3,那么计算机将其显示为1.29999999999是(相当(正确的......但这不是你会看到的...
现在的问题是计算机为什么要这样做?原因是计算机以 2 为基数(二进制(计算,而我们通常以 10 为基数(十进制(进行计算。结果是一样的(感谢上帝!(,但内部存储和表示不是。
有些数字以 10 为基数时看起来不错,例如 1.3,但其他数字则不然,例如 1/3 = 0.333333333....在基数 2 中也是如此,有些数字在基数 2 中"看起来"不错(通常由 2 的分数组成时(,而另一些则不然。当计算机在内部存储数字时,它可能无法"精确"存储它并存储最接近的可能表示形式,即使数字在十进制中看起来是"有限的"。所以是的,在这种情况下,它会"漂移"一点。如果你一次又一次地这样做,你可能会失去精度。但是没有其他方法(除非使用能够存储分数的特殊数学库(
当计算机试图以 10 为基数返回您给它的数字时,就会出现问题。然后计算机可能会给你 1.299999 而不是你预期的 1.3。
这也是为什么你不应该将浮点数与 ==、<、> 进行比较,而是使用特殊函数 islessgreater(a, b( isgreater(a, b( 等的原因。
所以你使用的实际函数(sprintf(很好,而且尽可能精确,它给你正确的值,你只需要知道在处理浮点数时,如果你期望1.3,那么最大精度的1.2999999是可以
的现在,如果你想"漂亮地打印"这些数字以获得最佳的"人类"表示(以10为基数(,你可能想要使用一个特殊的库,比如你的grisu3,它将尝试消除可能发生的漂移,并将数字对齐到最接近的10基数表示。
现在库无法使用水晶球来查找哪些数字漂移或不漂移,因此您可能会真正表示存储在计算机中的最高精度的 1.2999999,并且 lib 会将其"转换"为 1.3...但它并不比显示 1.29999 而不是 1.3 更差,也不更不精确。
如果您需要良好的可读性,这样的库将很有用。如果没有,那只是浪费时间。
希望这个帮助!
在任何合理的语言中执行此操作的最佳方法是:
- 使用语言的运行时库。 永远不要自己滚。 即使你有知识和好奇心来写它,你也不想测试它,你也不想维护它。
- 如果您发现运行时库转换有任何不当行为,请提交 bug。
- 如果这些转换是程序的可衡量瓶颈,请不要尝试使它们更快。 相反,找到一种方法来避免这样做。 与其将数字存储为字符串,不如只存储浮点数据(在可能控制字节序之后(。 如果需要字符串表示形式,请改用十六进制浮点格式。
我不是要劝阻你或任何人。 这些实际上是令人着迷的功能,但它们也非常复杂,并且试图为任何非天真的实现设计良好的测试覆盖率甚至更加复杂。 除非你准备花几个月的时间思考这个问题,否则不要开始。
您可能希望使用 Grisu 之类的东西(或更快的方法(,因为它为您提供了具有往返保证的最短十进制表示形式,这与仅采用固定精度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(。
同时,您可以使用 {fmt} 库,std::format
基于。{FMT} 还提供了 print
函数,使这更容易、更高效(Godbolt(:
fmt::print("{}", 0.3);
免责声明:我是 {fmt} 和 C++20 std::format
的作者。
C++你为什么不使用iostreams?您可能应该将 cout
用于控制台,ostringstream
用于面向字符串的输出(除非您非常具体地需要使用 printf
系列方法(。
您不必担心格式化性能,除非实际分析显示 CPU 是瓶颈(与 I/O 相比(。
void outputdouble( ostringstream & oss, double d )
{
oss.precision( 5 );
oss << d;
}
http://www.cplusplus.com/reference/iostream/ostringstream/
- 在VS代码中交叉编译Windows与Linux上的MinGW的SDL程序
- 如何为模板化对象创建模板向量?VS正在投掷C3203
- 数据成员SFINAE的C++17测试:gcc vs clang
- 为什么在Windows上的VS 2019和Clang 9中"size_t"在没有标题的情况下工作
- 在for循环中使用auto vs decltype(vec.size())来处理字符串的向量
- 正在VS调试器中监视映射条目
- Confusion: decltype vs std::function
- 将IBM Rhapsody模型集成到VS 2019中
- VS Code "command":"make"与终端窗口中的命令行"make"不同
- 使用VS Code和CMake Tools运行自定义命令
- 修改 VS Code 中的默认C++代码段
- 如何使用c++在VS 2019上运行SQL查询
- vs 2015 constexpr变量不恒定,但与2019相比还好吗
- 完美前进使用 std::forward vs RefRefCast
- 从VS 2015更新3更新到VS2015更新3 d后浮点计算行为不同的原因
- VS 2015 链接错误 无法构建依赖于 libcurl 的项目
- consteval wrapper vs. source_location
- VS Code C++:不准确的系统包括路径错误(wchar.h,boost/lambda/lambda.hpp)
- QStringList vs list<shared_ptr<QString>> 性能比较C++
- dtoa vs sprintf vs Grisu3 algorithm