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 的限制,并相应地对您的应用程序进行编码。您可能会考虑的一些选项包括:--
- 放弃使用
long double
数据类型,并按照 double
执行计算; (这是 only 选项,它 effectively 可供 Microsoft 编译器的用户使用,因为它实际上没有明显的 long double
开始的数据类型实现)。
- 按
long double
执行计算,但将结果转换为double
输出。
- 使用 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",因此作为编译器实现的最终用户,您不应该直接引用这些东西。
- 使用 C 的
snprintf
函数将 long double
数据转换为 C 字符串表示,然后使用 C++ 语义输出,而不是让 C++ 翻译 long double
的原始形式实体直接。 (IIRC,Microsoft 不提供 snprintf
——他们提供 _snprintf
——所以如果你小心使用 ANSI 函数名称,你会自动获得 80 位 long double
支持) .
考虑代码
#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 的限制,并相应地对您的应用程序进行编码。您可能会考虑的一些选项包括:--
- 放弃使用
long double
数据类型,并按照double
执行计算; (这是 only 选项,它 effectively 可供 Microsoft 编译器的用户使用,因为它实际上没有明显的long double
开始的数据类型实现)。 - 按
long double
执行计算,但将结果转换为double
输出。 - 使用 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",因此作为编译器实现的最终用户,您不应该直接引用这些东西。 - 使用 C 的
snprintf
函数将long double
数据转换为 C 字符串表示,然后使用 C++ 语义输出,而不是让 C++ 翻译long double
的原始形式实体直接。 (IIRC,Microsoft 不提供snprintf
——他们提供_snprintf
——所以如果你小心使用 ANSI 函数名称,你会自动获得 80 位long double
支持) .