2012-02-10 36 views
15

我想打印一个双精度值到std::cout(GCC,clang,MSVC++),使得输出在所有平台上都是相同的。双指数到C++ iostreams的便携式打印

我对指数的格式有问题。 下面的程序

#include <iostream> 
int main() 
{ 
    std::cout << 0.1e-7 << std::endl; 
    return 0; 
} 

有此输出与GCC:

1e-08 

,并与MSVC

1e-008 

以下输出怎样才能让这两个输出相同?

对不起,如果这是一个愚蠢的问题,但我到目前为止还没有找到答案。 所有格式似乎演变围绕前尾数一切的格式...

编辑:GCC的输出1e-08没有1e-8(原先规定),所以它符合。对困惑感到抱歉。

编辑2:实际上在Dietmar的评论后,将“尾数”改名为“指数”。 There also is a section on Wikipedia on mantissa vs. significant

+0

你有没有看[操纵](http://www.cplusplus.com/reference/iostream/manipulators/)? – razlebe 2012-02-10 10:40:11

+1

@razlebe:我找不到使用操纵器的答案。 – Manuel 2012-02-10 12:16:51

+0

我发现GCC不一致,因为它打印出1.e-08和1.e-18(两位数字),但它打印出1.e-256(三位数字)。我找不到解决此问题的流库(我尝试使用iostream和Boost.Format)。因此,如果想要固定宽度双打,需要为指数的可能的第三位保留余量空间。 – alfC 2017-02-24 04:30:45

回答

11

没有操纵者控制指数的格式(我假设你的意思是指数而不是尾数;而且,用于尾数的“官方”名称是显着)。更糟糕的是,我看不到C标准中限制指数格式的任何规则。我意识到这是关于C++的,但为了格式化细节的目的,C++标准指的是C标准。

我知道的唯一方法是使用自己的std::num_put<char>方面根据需要格式化值。然后这个方面将被放入std::locale,而被编入std::cout。潜在的实现可以使用缺省的std::num_put<char>构面(或者snprintf(),不幸的是,可能更简单)来格式化浮点数,然后去除指数中的前导零。

+4

C++格式化由'printf'定义,它表示“指数总是包含至少两位数”。 (所以g ++不符合。)我在这里阅读Posix标准,它应该符合C标准。但是,我确实对文本有模糊的记忆,说除非必要,它不能超过两个字符(这也会使VC错误)。我记得在很久很久以前就已经讨论过这个问题,并且确定VC是不符合的(如果这是您必须使用的编译器,则不能解决问题)。 – 2012-02-10 10:58:28

+1

@JamesKanze是否可以读取它,好像它们的意思是“字符”而不是数字,在这种情况下,减号会被计数? – Flexo 2012-02-10 11:03:05

+0

我只能简单地看一下C99标准(我没有更新的版本),但我没有找到指数的任何格式规则。在任何情况下,无论是符合还是不符合,似乎都有一些实施自由,并且在实施方面有明显差异。我仍然认为上述方法可能是透明地更改生成的格式的最简单方法。 – 2012-02-10 11:04:15

3

虽然迪特马尔的答案是干净的,也许只有真正便携的答案,我意外地发现了一个快速和肮脏的答案: MSVC提供_set_output_format功能,您可以使用切换到“打印指数以两位数字”。

以下RAII类可以在您的main()函数中实例化,以为您提供GCC,CLANG和MSVC的相同行为。

class ScientificNotationExponentOutputNormalizer 
{ 
public: 
    unsigned _oldExponentFormat; 

    ScientificNotationExponentOutputNormalizer() : _oldExponentFormat(0) 
    { 
#ifdef _MSC_VER 
     // Set scientific format to print two places. 
     unsigned _oldExponentFormat = _set_output_format(_TWO_DIGIT_EXPONENT); 
#endif 
    } 

    ~ScientificNotationExponentOutputNormalizer() 
    { 
#ifdef _MSC_VER 
     // Enable old exponent format. 
     _set_output_format(_oldExponentFormat); 
#endif 
    } 
}; 
+0

这需要至少MSVCR80.dll这是很痛苦的安装MinGW工作。 – Manuel 2012-03-03 18:10:22

+0

注意:如文档所述,“此功能已过时。从Visual Studio 2015开始,它在CRT中不可用 – BenC 2016-03-02 01:08:29

2

问题是Visual C++没有遵循C99标准。在Visual C++ 2015,_set_output_format除去,因为编译器现在遵循标准:

%e%E格式说明格式化为浮点数作为小数尾数和指数。在某些情况下,格式说明符%g%G也以此格式格式化数字。在以前的版本中,CRT将始终生成具有三位指数的字符串。例如,printf("%e\n", 1.0)将打印1.000000e+000这是不正确的:C要求如果指数只能用一个或两个数字表示,那么只需要打印两位数

在Visual Studio 2005中添加了全局一致性开关:_set_output_format。程序可以使用参数_TWO_DIGIT_EXPONENT调用此函数,以启用符合指数打印。 默认行为已更改为符合标准的指数打印模式

请参阅Breaking Changes in Visual C++ 2015。对于旧版本,请参阅@Manuel的答案。

仅供参考,在C99 standard,我们可以看到:

选择e,E

表示浮点数的双参数被转换在样式[ - ] d.ddd E(+ - )dd,在小数点前面有一位数字(如果参数非零,则为非零),其后的位数等于精度;如果精度丢失,则取6;如果精度为零并且未指定#标志,则不会显示小数点字符。该值被四舍五入为合适的位数。 E转换说明符使用E生成一个数字,而不是引入指数。 指数始终包含至少两位数字,并且只有表示指数所需的位数更多。如果该值为零,则指数为零。表示无穷大或NaN的双参数以f或F转换说明符的样式转换。

与C90相比,这与C90没有任何关于所需指数长度的指示有所不同。

注意到,最近的Visual C++的变化也涉及如何打印naninf