Long double 在 MinGW 上使用 iostreams 打印不正确

Long double is printed incorrectly with iostreams on MinGW

考虑代码

#include <iostream>

int main() {
  std::cout << 4.2L;
}

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

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

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

更新:

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

#include <cstdio>

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

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

不是 一个 MinGW 错误...尽管该声明看起来有争议,但实际上它是 Microsoft C/C++ 运行时库的限制, MinGW 依赖于它。作为一名开发人员,您有责任了解您的工具(例如这个工具)的局限性,并在这些限制内工作。

您遇到的问题是由于 Microsoft 在 MSVC 中缺少 long double 数据类型的任何独特实现,因此在 I/O 子系统中缺少对该数据类型的有效支持MSVCRT.DLL 提供; (而且,在你告诉我之前,也许是愤怒地,"of course MSVC supports long double",我知道它确实 语法,但 语义 它没有不同的实现,简单地通过有效地忽略 long 限定符来表现,使得 long double 成为裸 double).

的同义词

相反,GCC 和 MinGW 有一个 long double 的实现,这与 double 不同;前者是 80 位实体,而后者是 64 位实体,并且是 MSVC 的 both 数据类型的 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 执行计算; (这是 only 选项,它 effectively 可供 Microsoft 编译器的用户使用,因为它实际上没有明显的 long double开始的数据类型实现)。
  2. long double执行计算,但将结果转换为double输出。
  3. 使用 C 风格 I/O 函数而不是 C++ I/O 语义,并通过使用 -posix-D_GNU_SOURCE-D_BSD_SOURCE-D_XOPEN_SOURCE=700-D_POSIX_C_SOURCE=200809L,(或者更好的是,将 #define 用于后四个中的任何一个添加到您的来源, 在任何 #include)。如果愿意,您还可以用任何更早的合规级别替换 _XOPEN_SOURCE_POSIX_C_SOURCE; (但是,请忽略 非常糟糕的建议 ,某些评论员可能会提供使用 -D__USE_MINGW_ANSI_STDIO 进行编译;引入该宏名称的双下划线将其标记为"implementation reserved",因此作为编译器实现的最终用户,您不应该直接引用这些东西。
  4. 使用 C 的 snprintf 函数将 long double 数据转换为 C 字符串表示,然后使用 C++ 语义输出,而不是让 C++ 翻译 long double 的原始形式实体直接。 (IIRC,Microsoft 不提供 snprintf——他们提供 _snprintf——所以如果你小心使用 ANSI 函数名称,你会自动获得 80 位 long double 支持) .