MinGW上的iostream错误地打印了长双精度 [英] Long double is printed incorrectly with iostreams on MinGW
问题描述
考虑代码
#include <iostream>
int main() {
std::cout << 4.2L;
}
在 MinGW 上进行编译,并在以下输出中运行结果:
Compiling it on MinGW and running results in the following output:
> g++ test.cc
> a.exe
-7.89773e-278
这是MinGW中的错误,是否有修复程序或解决方法?
Is it a bug in MinGW and is there a fix or workaround?
更新:
在此问题中描述的与printf
类似的问题:
There is a similar issue with printf
described in this question:
#include <cstdio>
int main() {
std::printf("%Lg", 4.2L); // prints -7.89773e-278
}
但是,可以通过定义__USE_MINGW_ANSI_STDIO
来解决printf
的问题,而不能定义此问题,因此我认为这值得一个单独的问题.
However, the issue with printf
can be fixed by defining __USE_MINGW_ANSI_STDIO
while this one can't, so I think it deserves a separate question.
推荐答案
这不是不是 MinGW错误...该声明似乎引起争议,事实是这是Microsoft的限制MinGW所依赖的C/C ++运行时库.作为开发人员,您有责任了解工具(例如此工具)的局限性,并在这些局限性内工作.
This is not a MinGW bug ... controversial as that statement may seem, the reality is that it is a limitation of Microsoft's C/C++ runtime library, upon which MinGW is dependent. As a developer, it is incumbent on you to understand the limitations of your tools, such as this one, and to work within those limitations.
您遇到的问题是由于Microsoft在MSVC中缺少对long double
数据类型的任何不同实现,以及由于在MSVCRT.DLL提供的I/O子系统中对该数据类型缺乏有效的支持; (而且,在您可能不屑一顾地告诉我之前,当然MSVC支持long double
",我知道它确实语法上,但是科学上没有明显的实现,只需通过有效地忽略long
限定词即可,从而使long double
成为裸double
的同义词).
The problem you are experiencing is due to Microsoft's lack of any distinct implementation of a long double
data type in MSVC, and the consequent lack of effective support for that data type within the I/O subsystem provided by MSVCRT.DLL; (and, before you tell me, perhaps indignantly, that "of course MSVC supports long double
", I know that it does syntactically, but semantically it has no distinct implementation, simply behaving by effectively ignoring the long
qualifier, such that long double
becomes a synonym for bare double
).
相反,GCC以及因此的MinGW 具有long double
的实现,这与double
不同;前者是80位实体,而后者是64位实体,与MSVC的 both 数据类型的64位实现完全类似.当您需要更高精度的80位浮点计算时,这很好,但是在结果输出时,可能会导致诸如您正在遇到的问题. (I/O转换器将long double
数据实体的80位原始数据表示形式传递给它,期望在这里为64位;内部表示形式存在不兼容的差异,因此当部分尾数被解释为指数时会产生垃圾)
Conversely, GCC and hence MinGW does have an implementation of long double
, which is distinct from double
; the former is an 80-bit entity, whereas the latter is 64-bit, and is an exact analogue of MSVC's 64-bit implementation of both data types. This is great when you need the greater precision of 80-bit floating point computation, but it can lead to problems, such as you are experiencing, when it comes to output of results; (the I/O translator is handed an 80-bit raw data representation of a long double
data entity, where it expects 64-bit; the internal representations are incompatibly different, so garbage ensues when part of the mantissa is interpreted as exponent).
您已经注意到,虽然MSVCRT.DLL仅支持64位double
值的输出,但MinGW确实提供了C的printf
样式I/O的替代实现,该实现可以正确转换80位格式;但是,这并不扩展到支持C ++风格的I/O.因此,在C ++代码中,您不能在继续使用C ++ I/O语义的同时简单地利用MinGW替代I/O实现.您必须识别MSVCRT.DLL的限制,并相应地编写应用程序代码.您可能会考虑的一些选项包括:-
As you've noted, whereas MSVCRT.DLL supports only output of 64-bit double
values, MinGW does offer an alternative implementation of C's printf
style I/O, which can correctly translate the 80-bit format; however, this does not extend to supporting C++ style I/O. Thus, in C++ code, you cannot simply exploit the MinGW alternative I/O implementation, while continuing to use C++ I/O semantics; you have to recognize the MSVCRT.DLL limitation, and code your application accordingly. Some options you might consider include:--
- 放弃使用
long double
数据类型,并以double
的方式执行计算; (这是 only 选项,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
一起编译;引入该宏名称的双下划线将其标记为实现保留",因此,作为编译器实现的最终用户,您不应直接引用这些内容. - 使用C的
snprintf
函数将long double
数据转换为C字符串表示形式,然后使用C ++语义进行输出,而不必离开C ++直接转换long double
实体的原始形式. (IIRC,Microsoft不提供snprintf
,而是提供_snprintf
,因此,如果您谨慎使用ANSI函数名称,则会自动获得80位的long double
支持.)
- Forego the use of the
long double
data type, and perform your computations asdouble
; (this is the only option which is effectively available to users of Microsoft's compiler, because it doesn't really have a distinctlong double
data type implementation to begin with). - Perform the computation as
long double
, but cast the result todouble
for output. - Use C style I/O functions instead of the C++ I/O semantics, and enable MinGW's alternative
printf
implementation by compiling with any of-posix
,-D_GNU_SOURCE
,-D_BSD_SOURCE
,-D_XOPEN_SOURCE=700
, or-D_POSIX_C_SOURCE=200809L
, (or better, add#define
for any of the latter four to your source, before any#include
). If preferred, you may also substitute any earlier compliance level for_XOPEN_SOURCE
or_POSIX_C_SOURCE
; (however, please ignore the incredibly bad advice, which may be offered by some commentators, to compile with-D__USE_MINGW_ANSI_STDIO
; the double underscore, which introduces that macro name, marks it as "implementation reserved", and therefore not something which you, as an end user of the compiler implementation, should be referring to directly). - Use C's
snprintf
function to convertlong double
data to a C string representation, then use C++ semantics to output that instead of leaving C++ to translate the raw form of thelong double
entity directly. (IIRC, Microsoft don't providesnprintf
-- they provide_snprintf
instead -- so if you are careful to use the ANSI function name, you get 80-bitlong double
support automatically).
这篇关于MinGW上的iostream错误地打印了长双精度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!