长双精度在 MinGW 上打印错误

Long double is printed incorrectly with iostreams on MinGW

本文关键字:打印 错误 MinGW 双精度      更新时间:2023-10-16

考虑代码

#include <iostream>
int main() {
std::cout << 4.2L;
}

在 MinGW 上编译并运行它会产生以下输出:

> g++ test.cc
> a.exe
-7.89773e-278

这是MinGW中的错误吗,是否有修复或解决方法?

更新:

此问题中描述的printf也存在类似的问题:

#include <cstdio>
int main() {
std::printf("%Lg", 4.2L); // prints -7.89773e-278
}

但是,printf的问题可以通过定义__USE_MINGW_ANSI_STDIO来解决,而这个不能,所以我认为它值得一个单独的问题。

这不是MinGW的错误...尽管这种说法看起来很有争议,但现实情况是,它是 MinGW 所依赖的 Microsoft C/C++ 运行时库的限制。作为开发人员,您有责任了解工具(例如此工具)的局限性,并在这些限制范围内工作。

您遇到的问题是由于 Microsoft 在 MSVC 中缺少对long double数据类型的任何明显实现,因此在 MSVCRT.DLL 提供的 I/O 子系统中缺乏对该数据类型的有效支持;(而且,在您告诉我之前,也许是愤慨地告诉我"MSVC 当然支持long double",我知道它在语法上确实如此,但在语义上它没有明显的实现,只是通过有效地忽略long限定符来表现,因此long double成为裸double的同义词)。

相反,GCC和MinGW确实有一个long double的实现,这与double不同;前者是一个80位的实体,而后者是64位的,并且是MSVC两种数据类型的64位实现的精确类似物。当您需要更高精度的 80 位浮点计算时,这非常有用,但在结果输出方面可能会导致问题,例如您正在遇到的问题;(I/O 转换器将获得long double数据实体的 80 位原始数据表示形式,它期望 64 位;内部表示形式不兼容,因此当尾数的一部分被解释为指数时,垃圾随之而来)。

正如你所注意到的,虽然MSVCRT.DLL仅支持64位double值的输出,但MinGW确实提供了C的printf样式I/O的替代实现,它可以正确转换80位格式;但是,这并不扩展到支持C++样式I/O。因此,在C++代码中,您不能简单地利用 MinGW 替代 I/O 实现,同时继续使用 C++ I/O 语义;您必须认识到 MSVCRT.DLL 限制,并相应地对应用程序进行编码。您可以考虑的一些选项包括:--

  1. 放弃使用long double数据类型,并按double执行计算;(这是Microsoft编译器用户有效使用的唯一选项,因为它实际上没有明显的long double数据类型实现)。
  2. long double执行计算,但将结果转换为double进行输出。
  3. 使用 C 样式的 I/O
  4. 函数代替C++ I/O 语义,并通过编译-posix-D_GNU_SOURCE-D_BSD_SOURCE-D_XOPEN_SOURCE=700-D_POSIX_C_SOURCE=200809L中的任何一个来启用 MinGW 的替代printf实现(或者更好的是,在任何#include之前将后四个中的任何一个#define中的任何一个添加到您的源中)。 如果愿意,您也可以用任何较早的合规级别代替_XOPEN_SOURCE_POSIX_C_SOURCE;(但是,请忽略一些评论员可能提供的令人难以置信的糟糕建议,即使用-D__USE_MINGW_ANSI_STDIO进行编译;引入该宏名称的双下划线将其标记为"保留的实现",因此您作为编译器实现的最终用户不应该直接引用)。
  5. 使用 C 的
  6. snprintf函数将long double数据转换为 C 字符串表示形式,然后使用C++语义输出该表示形式,而不是让C++直接翻译long double实体的原始形式。(IIRC,Microsoft不提供snprintf- 而是提供_snprintf- 因此,如果您小心使用ANSI函数名称,则会自动获得80位long double支持)。