【问题标题】:Long double is printed incorrectly with iostreams on MinGW使用 MinGW 上的 iostreams 错误地打印长双精度
【发布时间】:2015-05-06 15:06:02
【问题描述】:

考虑代码

#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 来解决,而这个不能,所以我认为它值得一个单独的问题。

【问题讨论】:

  • 可能是个bug,查看这个问题的答案link
  • 这似乎与linked question 中描述的错误几乎相同。 MinGW 使用 gcc 编译器和微软的运行时库;他们对long double 有不同的表示。得分最高的答案(不是公认的答案)提到了printf 的解决方法,使用-D__USE_MINGW_ANSI_STDIO;我很想知道cout &lt;&lt; ... 是否有类似的解决方法。
  • 感谢您的链接。我刚刚检查过,printf 也存在类似的问题,所以它可能是相关的。
  • 不幸的是 __USE_MINGW_ANSI_STDIO 只修复了 printf 的输出。我已经更新了问题。
  • 此代码在 mingw-w64 4.9.2(32 位和 64 位)中正常工作

标签: c++ mingw iostream


【解决方案1】:

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

您遇到的问题是由于 Microsoft 在 MSVC 中缺乏对 long double 数据类型的任何独特实现,因此在 MSVCRT.DLL 提供的 I/O 子系统中缺乏对该数据类型的有效支持; (而且,在你告诉我,也许是愤怒地告诉我“MSVC 当然支持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 选项,它有效 可供 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 或 @ 中的任何一个进行编译来启用 MinGW 的替代 printf 实现987654341@,(或者更好的是,将#define 添加到您的源代码中,在任何之前 #include)。如果愿意,您也可以将任何早期的合规级别替换为_XOPEN_SOURCE_POSIX_C_SOURCE; (但是,请忽略某些评论员可能提出的非常糟糕的建议,以使用-D__USE_MINGW_ANSI_STDIO 进行编译;引入该宏名称的双下划线将其标记为“保留实现”,因此,作为编译器实现的最终用户,您不应该直接引用它)。
  4. 使用 C 的 snprintf 函数将 long double 数据转换为 C 字符串表示,然后使用 C++ 语义来输出,而不是让 C++ 直接转换 long double 实体的原始形式。 (IIRC,微软不提供snprintf——他们提供_snprintf——所以如果你小心使用ANSI函数名,你会自动获得80位long double支持)。

【讨论】:

  • 您关于微软实施的说法不正确。 Microsoft 的编译器和运行时 do 实现了long double 类型;它恰好与double 具有相同的表示同时仍然是一个不同的类型。同样,shortint,或intlong,或longlong long 通常具有相同的大小,但它们仍然是不同的类型,没有人说编译器没有t 实现 long 如果它恰好与 int 大小相同。
  • 微软的编译器和运行时一致且正确;是 MS 运行时和 gcc 编译器的混合导致了 MinGW 中的这个错误。
  • 这是一个 MinGW 错误,因为它是 MinGW 展示的一个错误。责备别人并不能改变这一点。 MinGW 开发人员本可以选择不将繁重的工作交给其他无法完成正确工作的功能。事实上,他们在 printf 版本中正是这样做的,为用户提供了获得合规行为或 MS-forwarding-buggy 行为的选项。
  • 所以,如果您认为这是一个错误,请提交错误报告;在这样一个根本不相关的论坛上发牢骚,一无所获。
  • @KeithMarshall 他们正在写评论告诉你你错了,你没有更正你的答案或解决他们,你只是忽略了他们的评论。他们很可能已经提交了错误报告,但他们的评论告诉您他们认为您的第一句话是错误的,这是完全合法的。真正的错误也许可以说是向上传播的,因此将其识别为您所拥有的会更准确,但我认为他没有说这不是 mingw 错误是有道理的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-07
  • 2021-07-08
相关资源
最近更新 更多