MinGW上的iostream错误地打印了长双精度 [英] Long double is printed incorrectly with iostreams on MinGW

查看:111
本文介绍了MinGW上的iostream错误地打印了长双精度的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑代码

#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:--

  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-D_POSIX_C_SOURCE=200809L,(或更好的方法是,将后四个中的任何一个#define添加到您的源文件中,之前 #include).如果愿意,您也可以用任何较早的合规性级别代替_XOPEN_SOURCE_POSIX_C_SOURCE; (但是,请忽略某些注释者可能会提供的非常糟糕的建议,以使其与-D__USE_MINGW_ANSI_STDIO一起编译;引入该宏名称的双下划线将其标记为实现保留",因此,作为编译器实现的最终用户,您不应直接引用这些内容.
  4. 使用C的snprintf函数将long double数据转换为C字符串表示形式,然后使用C ++语义进行输出,而不必离开C ++直接转换long double实体的原始形式. (IIRC,Microsoft不提供snprintf,而是提供_snprintf,因此,如果您谨慎使用ANSI函数名称,则会自动获得80位的long double支持.)
  1. Forego the use of the long double data type, and perform your computations as double; (this is the only option which is effectively available to users of Microsoft's compiler, because it doesn't really have a distinct long double data type implementation to begin with).
  2. Perform the computation as long double, but cast the result to double for output.
  3. 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).
  4. Use C's snprintf function to convert long double data to a C string representation, then use C++ semantics to output that instead of leaving C++ to translate the raw form of the long double entity directly. (IIRC, Microsoft don't provide snprintf -- they provide _snprintf instead -- so if you are careful to use the ANSI function name, you get 80-bit long double support automatically).

这篇关于MinGW上的iostream错误地打印了长双精度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆